making clangd happy and making script to compile/build with run.sh
@@ -0,0 +1 @@
|
||||
build/
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(documentviewer LANGUAGES CXX)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets
|
||||
OPTIONAL_COMPONENTS PrintSupport Pdf PdfWidgets)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
add_compile_definitions(QT_NO_CAST_FROM_ASCII)
|
||||
|
||||
if(NOT DEFINED INSTALL_EXAMPLESDIR)
|
||||
set(INSTALL_EXAMPLESDIR "examples")
|
||||
endif()
|
||||
|
||||
if(TARGET Qt6::PrintSupport)
|
||||
add_compile_definitions(QT_DOCUMENTVIEWER_PRINTSUPPORT)
|
||||
endif()
|
||||
|
||||
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/demos/documentviewer")
|
||||
|
||||
add_subdirectory(app)
|
||||
add_subdirectory(plugins)
|
||||
@@ -0,0 +1,417 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 12.0.2, 2024-02-07T12:39:19. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{c8be0ad5-6296-489b-9771-86900341c424}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="qlonglong">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
|
||||
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.CTest">false</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">4</value>
|
||||
<value type="bool" key="ClangTools.PreferConfigFile">true</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 6.6.1 GCC 64bit</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 6.6.1 GCC 64bit</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt6.661.gcc_64_kit</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
|
||||
<value type="QString" key="CMake.Build.Type">Debug</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_BUILD_TYPE:STRING=Debug
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="int" key="EnableQmlDebugging">0</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/talksik/Qt/Examples/Qt-6.6.1/demos/build-documentviewer-Desktop_Qt_6_6_1_GCC_64bit-Debug</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
|
||||
<value type="QString" key="CMake.Build.Type">Release</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_BUILD_TYPE:STRING=Release
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/talksik/Qt/Examples/Qt-6.6.1/demos/build-documentviewer-Desktop_Qt_6_6_1_GCC_64bit-Release</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
|
||||
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/talksik/Qt/Examples/Qt-6.6.1/demos/build-documentviewer-Desktop_Qt_6_6_1_GCC_64bit-RelWithDebInfo</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release with Debug Information</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.3">
|
||||
<value type="QString" key="CMake.Build.Type">RelWithDebInfo</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="int" key="EnableQmlDebugging">0</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/talksik/Qt/Examples/Qt-6.6.1/demos/build-documentviewer-Desktop_Qt_6_6_1_GCC_64bit-Profile</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.4">
|
||||
<value type="QString" key="CMake.Build.Type">MinSizeRel</value>
|
||||
<value type="int" key="CMake.Configure.BaseEnvironment">2</value>
|
||||
<value type="bool" key="CMake.Configure.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMake.Configure.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="CMake.Initial.Parameters">-DCMAKE_GENERATOR:STRING=Ninja
|
||||
-DCMAKE_BUILD_TYPE:STRING=MinSizeRel
|
||||
-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake
|
||||
-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable}
|
||||
-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX}
|
||||
-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C}
|
||||
-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx}
|
||||
-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG}</value>
|
||||
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/talksik/Qt/Examples/Qt-6.6.1/demos/build-documentviewer-Desktop_Qt_6_6_1_GCC_64bit-MinSizeRel</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">all</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
|
||||
<value type="QString" key="CMakeProjectManager.MakeStep.BuildPreset"></value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.BuildTargets">
|
||||
<value type="QString">clean</value>
|
||||
</valuelist>
|
||||
<value type="bool" key="CMakeProjectManager.MakeStep.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="CMakeProjectManager.MakeStep.UserEnvironmentChanges"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.MakeStep</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
|
||||
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
|
||||
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Minimum Size Release</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">CMakeProjectManager.CMakeBuildConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">5</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Valgrind.Callgrind.CostFormat">0</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
|
||||
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
</valuemap>
|
||||
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="qlonglong">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
||||
@@ -0,0 +1,68 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(documentviewer LANGUAGES CXX)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
qt_standard_project_setup()
|
||||
|
||||
qt_add_library(abstractviewer STATIC
|
||||
abstractviewer.cpp abstractviewer.h
|
||||
)
|
||||
|
||||
qt_add_executable(documentviewer
|
||||
main.cpp
|
||||
mainwindow.cpp mainwindow.h mainwindow.ui
|
||||
viewerfactory.cpp viewerfactory.h
|
||||
recentfiles.cpp recentfiles.h
|
||||
recentfilemenu.cpp recentfilemenu.h
|
||||
viewerinterfaces.h
|
||||
documentviewer.qrc
|
||||
)
|
||||
|
||||
set_target_properties(documentviewer PROPERTIES
|
||||
WIN32_EXECUTABLE TRUE
|
||||
MACOSX_BUNDLE TRUE
|
||||
)
|
||||
|
||||
target_link_libraries(documentviewer PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
abstractviewer
|
||||
)
|
||||
|
||||
target_link_libraries(abstractviewer PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
)
|
||||
|
||||
set(plugin_targets
|
||||
jsonviewer
|
||||
txtviewer
|
||||
)
|
||||
|
||||
if(TARGET pdfviewer)
|
||||
list(APPEND plugin_targets pdfviewer)
|
||||
endif()
|
||||
|
||||
if(QT6_IS_SHARED_LIBS_BUILD)
|
||||
add_dependencies(documentviewer ${plugin_targets})
|
||||
else()
|
||||
target_link_libraries(documentviewer PRIVATE ${plugin_targets})
|
||||
endif()
|
||||
|
||||
if(TARGET Qt6::PrintSupport)
|
||||
target_link_libraries(documentviewer PRIVATE Qt6::PrintSupport)
|
||||
target_link_libraries(abstractviewer PRIVATE Qt6::PrintSupport)
|
||||
add_compile_definitions(QT_DOCUMENTVIEWER_PRINTSUPPORT)
|
||||
endif()
|
||||
|
||||
install(TARGETS documentviewer
|
||||
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "abstractviewer.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollArea>
|
||||
#include <QStatusBar>
|
||||
#include <QTabWidget>
|
||||
#include <QToolBar>
|
||||
|
||||
#include <QAction>
|
||||
|
||||
#include <QFile>
|
||||
#include <QSettings>
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
#include <QPrinter>
|
||||
#include <QPrintDialog>
|
||||
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
AbstractViewer::AbstractViewer() : m_file(nullptr), m_widget(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void AbstractViewer::init(QFile *file, QWidget *widget, QMainWindow *mainWindow)
|
||||
{
|
||||
m_file.reset(file);
|
||||
m_widget = widget;
|
||||
Q_ASSERT(widget);
|
||||
Q_ASSERT(mainWindow);
|
||||
m_uiAssets.mainWindow = mainWindow;
|
||||
}
|
||||
|
||||
AbstractViewer::~AbstractViewer()
|
||||
{
|
||||
AbstractViewer::cleanup();
|
||||
}
|
||||
|
||||
bool AbstractViewer::isEmpty() const
|
||||
{
|
||||
return !hasContent();
|
||||
}
|
||||
|
||||
bool AbstractViewer::isPrintingEnabled() const
|
||||
{
|
||||
return m_printingEnabled;
|
||||
}
|
||||
|
||||
bool AbstractViewer::hasContent() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractViewer::supportsOverview() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractViewer::isModified() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractViewer::saveDocument()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AbstractViewer::saveDocumentAs()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QAction *> AbstractViewer::actions() const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
QWidget *AbstractViewer::widget() const
|
||||
{
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
QList<QMenu *> AbstractViewer::menus() const
|
||||
{
|
||||
return m_menus;
|
||||
}
|
||||
|
||||
QMainWindow *AbstractViewer::mainWindow() const
|
||||
{
|
||||
return m_uiAssets.mainWindow;
|
||||
}
|
||||
|
||||
QStatusBar *AbstractViewer::statusBar() const
|
||||
{
|
||||
return mainWindow()->statusBar();
|
||||
}
|
||||
|
||||
QMenuBar *AbstractViewer::menuBar() const
|
||||
{
|
||||
return mainWindow()->menuBar();
|
||||
}
|
||||
|
||||
void AbstractViewer::maybeEnablePrinting()
|
||||
{
|
||||
maybeSetPrintingEnabled(true);
|
||||
}
|
||||
|
||||
void AbstractViewer::disablePrinting()
|
||||
{
|
||||
maybeSetPrintingEnabled(false);
|
||||
}
|
||||
|
||||
bool AbstractViewer::isDefaultViewer() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AbstractViewer *AbstractViewer::viewer()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
const AbstractViewer *AbstractViewer::viewer() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
void AbstractViewer::statusMessage(const QString &message, const QString &type, int timeout)
|
||||
{
|
||||
const QString msg = viewerName()
|
||||
+ (type.isEmpty() ? ": "_L1 : "/"_L1 + type + ": "_L1) + message;
|
||||
emit showMessage(msg, timeout);
|
||||
}
|
||||
|
||||
QToolBar *AbstractViewer::addToolBar(const QString &title)
|
||||
{
|
||||
auto *bar = mainWindow()->addToolBar(title);
|
||||
QString name = title;
|
||||
name.remove(u' ');
|
||||
bar->setObjectName(name);
|
||||
m_toolBars.append(bar);
|
||||
return bar;
|
||||
}
|
||||
|
||||
QMenu *AbstractViewer::addMenu(const QString &title)
|
||||
{
|
||||
QMenu *menu = new QMenu(title, menuBar());
|
||||
menu->setObjectName(title);
|
||||
menuBar()->insertMenu(m_uiAssets.help, menu);
|
||||
m_menus.append(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
void AbstractViewer::cleanup()
|
||||
{
|
||||
// delete all objects created by the viewer which need to be displayed
|
||||
// and therefore parented on MainWindow
|
||||
if (m_file)
|
||||
m_file.reset();
|
||||
|
||||
qDeleteAll(m_menus);
|
||||
m_menus.clear();
|
||||
qDeleteAll(m_toolBars);
|
||||
m_toolBars.clear();
|
||||
|
||||
for (const auto &connection : m_connections)
|
||||
QObject::disconnect(connection);
|
||||
|
||||
m_connections.clear();
|
||||
}
|
||||
|
||||
QMenu *AbstractViewer::fileMenu()
|
||||
{
|
||||
static constexpr QLatin1StringView name = QLatin1StringView("qtFileMenu");
|
||||
static QMenu *fileMenu = nullptr;
|
||||
if (fileMenu)
|
||||
return fileMenu;
|
||||
|
||||
const QList<QMenu *> menus = mainWindow()->findChildren<QMenu *>();
|
||||
for (auto *menu : menus) {
|
||||
if (menu->objectName() == name) {
|
||||
fileMenu = menu;
|
||||
return fileMenu;
|
||||
}
|
||||
}
|
||||
fileMenu = addMenu(tr("&File"));
|
||||
fileMenu->setObjectName(name);
|
||||
return fileMenu;
|
||||
}
|
||||
|
||||
void AbstractViewer::print()
|
||||
{
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
static const QString type = tr("Printing");
|
||||
if (!hasContent()) {
|
||||
statusMessage(tr("No content to print."), type);
|
||||
return;
|
||||
}
|
||||
|
||||
QPrinter printer(QPrinter::HighResolution);
|
||||
QPrintDialog dlg(&printer, mainWindow());
|
||||
dlg.setWindowTitle(tr("Print Document"));
|
||||
if (dlg.exec() == QDialog::Accepted) {
|
||||
printDocument(&printer);
|
||||
} else {
|
||||
statusMessage(tr("Printing canceled!"), type);
|
||||
return;
|
||||
}
|
||||
|
||||
const QPrinter::PrinterState state = printer.printerState();
|
||||
QString message = viewerName() + " :"_L1;
|
||||
switch (state) {
|
||||
case QPrinter::PrinterState::Aborted:
|
||||
message += tr("Printing aborted.");
|
||||
break;
|
||||
case QPrinter::PrinterState::Active:
|
||||
message += tr("Printing active.");
|
||||
break;
|
||||
case QPrinter::PrinterState::Idle:
|
||||
message += tr("Printing completed.");
|
||||
break;
|
||||
case QPrinter::PrinterState::Error:
|
||||
message += tr("Printing error.");
|
||||
break;
|
||||
}
|
||||
statusMessage(message, type);
|
||||
#else
|
||||
statusMessage(tr("Printing not supported!"));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief AbstractViewer::setPrintingEnabled
|
||||
Enables / disables printing.
|
||||
If printing is not supported or the viewer has no content to display,
|
||||
\param enabled is overridden with \c false;
|
||||
The signal printingEnabledChanged is emitted if the status has changed.
|
||||
*/
|
||||
void AbstractViewer::maybeSetPrintingEnabled(bool enabled)
|
||||
{
|
||||
#ifndef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
enabled = false;
|
||||
#else
|
||||
if (!hasContent())
|
||||
enabled = false;
|
||||
#endif
|
||||
|
||||
if (enabled == m_printingEnabled)
|
||||
return;
|
||||
|
||||
m_printingEnabled = enabled;
|
||||
emit printingEnabledChanged(enabled);
|
||||
}
|
||||
|
||||
void AbstractViewer::initViewer(QAction *back, QAction *forward, QAction *help, QTabWidget *tabs)
|
||||
{
|
||||
// Viewers need back & forward buttons and a tab widget.
|
||||
Q_ASSERT(back);
|
||||
Q_ASSERT(forward);
|
||||
Q_ASSERT(tabs);
|
||||
Q_ASSERT(help);
|
||||
|
||||
m_uiAssets.back = back;
|
||||
m_uiAssets.forward = forward;
|
||||
m_uiAssets.help = help;
|
||||
m_uiAssets.tabs = tabs;
|
||||
|
||||
// Tabs can be populated individually by the viewer, if it supports overview
|
||||
tabs->clear();
|
||||
tabs->setVisible(supportsOverview());
|
||||
|
||||
emit uiInitialized();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef ABSTRACTVIEWER_H
|
||||
#define ABSTRACTVIEWER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
class QFile;
|
||||
class QMainWindow;
|
||||
class QMenu;
|
||||
class QMenuBar;
|
||||
class QPrinter;
|
||||
class QToolBar;
|
||||
class QTabWidget;
|
||||
class QScrollArea;
|
||||
class QStatusBar;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class AbstractViewer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected:
|
||||
AbstractViewer();
|
||||
|
||||
public:
|
||||
virtual ~AbstractViewer();
|
||||
virtual void init(QFile *file, QWidget *widget, QMainWindow *mainWindow);
|
||||
void initViewer(QAction *back, QAction *forward, QAction *help, QTabWidget *tabs);
|
||||
virtual bool isModified() const;
|
||||
virtual bool saveDocument();
|
||||
virtual bool saveDocumentAs();
|
||||
virtual QString viewerName() const = 0;
|
||||
virtual bool supportsOverview() const;
|
||||
virtual QByteArray saveState() const = 0;
|
||||
virtual bool restoreState(QByteArray &) = 0;
|
||||
virtual bool hasContent() const;
|
||||
virtual QStringList supportedMimeTypes() const = 0;
|
||||
virtual bool isDefaultViewer() const;
|
||||
virtual void cleanup();
|
||||
bool isEmpty() const;
|
||||
bool isPrintingEnabled() const;
|
||||
AbstractViewer *viewer();
|
||||
const AbstractViewer *viewer() const;
|
||||
|
||||
QList<QAction *> actions() const;
|
||||
QWidget *widget() const;
|
||||
QList<QMenu *> menus() const;
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
protected:
|
||||
virtual void printDocument(QPrinter *) const {};
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void uiInitialized();
|
||||
void printingEnabledChanged(bool enabled);
|
||||
void showMessage(const QString &message, int timeout = 8000);
|
||||
void documentLoaded(const QString &fileName);
|
||||
|
||||
public slots:
|
||||
void print();
|
||||
|
||||
protected:
|
||||
|
||||
struct UiAssets {
|
||||
QMainWindow *mainWindow = nullptr;
|
||||
QAction *back = nullptr;
|
||||
QAction *forward = nullptr;
|
||||
QAction *help = nullptr;
|
||||
QTabWidget *tabs = nullptr;
|
||||
} m_uiAssets;
|
||||
|
||||
void statusMessage(const QString &message, const QString &type = QString(), int timeout = 8000);
|
||||
QToolBar *addToolBar(const QString &);
|
||||
QMenu *addMenu(const QString &);
|
||||
QMenu *fileMenu();
|
||||
QMainWindow *mainWindow() const;
|
||||
QStatusBar *statusBar() const;
|
||||
QMenuBar *menuBar() const;
|
||||
|
||||
std::unique_ptr<QFile> m_file;
|
||||
QList<QAction *> m_actions;
|
||||
QWidget *m_widget = nullptr;
|
||||
QList<QMetaObject::Connection> m_connections;
|
||||
|
||||
protected slots:
|
||||
void maybeSetPrintingEnabled(bool enabled);
|
||||
void maybeEnablePrinting();
|
||||
void disablePrinting();
|
||||
|
||||
private:
|
||||
QList<QMenu *> m_menus;
|
||||
QList<QToolBar *> m_toolBars;
|
||||
bool m_printingEnabled = false;
|
||||
};
|
||||
|
||||
#endif // ABSTRACTVIEWER_H
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="demos/documentviewer">
|
||||
<file>images/copy@2x.png</file>
|
||||
<file>images/copy.png</file>
|
||||
<file>images/cut@2x.png</file>
|
||||
<file>images/cut.png</file>
|
||||
<file>images/go-next-view@2x.png</file>
|
||||
<file>images/go-next-view-page@2x.png</file>
|
||||
<file>images/go-next-view-page.png</file>
|
||||
<file>images/go-next-view.png</file>
|
||||
<file>images/go-previous-view@2x.png</file>
|
||||
<file>images/go-previous-view-page@2x.png</file>
|
||||
<file>images/go-previous-view-page.png</file>
|
||||
<file>images/go-previous-view.png</file>
|
||||
<file>images/magnifier@2x.png</file>
|
||||
<file>images/magnifier.png</file>
|
||||
<file>images/open@2x.png</file>
|
||||
<file>images/open.png</file>
|
||||
<file>images/paste@2x.png</file>
|
||||
<file>images/paste.png</file>
|
||||
<file>images/print2x.png</file>
|
||||
<file>images/print.png</file>
|
||||
<file>images/qt-logo@2x.png</file>
|
||||
<file>images/qt-logo.png</file>
|
||||
<file>images/zoom-fit-best@2x.png</file>
|
||||
<file>images/zoom-fit-best.png</file>
|
||||
<file>images/zoom-fit-width@2x.png</file>
|
||||
<file>images/zoom-fit-width.png</file>
|
||||
<file>images/zoom-in@2x.png</file>
|
||||
<file>images/zoom-in.png</file>
|
||||
<file>images/zoom-original@2x.png</file>
|
||||
<file>images/zoom-original.png</file>
|
||||
<file>images/zoom-out@2x.png</file>
|
||||
<file>images/zoom-out.png</file>
|
||||
<file>images/zoom-previous@2x.png</file>
|
||||
<file>images/zoom-previous.png</file>
|
||||
<file>images/document-open-recent.svgz</file>
|
||||
<file>images/go-next.svgz</file>
|
||||
<file>images/go-previous.svgz</file>
|
||||
<file>images/help-about.svgz</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 990 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1010 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1006 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1017 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 678 B |
|
After Width: | Height: | Size: 931 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 952 B |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 946 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 940 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 946 B |
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
QCoreApplication::setOrganizationName("QtProject"_L1);
|
||||
QCoreApplication::setApplicationName("DocumentViewer"_L1);
|
||||
QCoreApplication::setApplicationVersion("1.0"_L1);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QApplication::translate("main",
|
||||
"A viewer for JSON, PDF and text files"));
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.addPositionalArgument("File"_L1, QApplication::translate("main",
|
||||
"JSON, PDF or text file to open"));
|
||||
parser.process(app);
|
||||
|
||||
const QStringList &positionalArguments = parser.positionalArguments();
|
||||
const QString &fileName = (positionalArguments.count() > 0) ? positionalArguments.at(0)
|
||||
: QString();
|
||||
|
||||
MainWindow w;
|
||||
w.show();
|
||||
if (!fileName.isEmpty())
|
||||
w.openFile(fileName);
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "viewerfactory.h"
|
||||
#include "abstractviewer.h"
|
||||
#include "recentfiles.h"
|
||||
#include "recentfilemenu.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QToolButton>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <QDir>
|
||||
#include <QSettings>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::onActionOpenTriggered);
|
||||
connect(ui->actionAbout, &QAction::triggered, this, &MainWindow::onActionAboutTriggered);
|
||||
connect(ui->actionAboutQt, &QAction::triggered, this, &MainWindow::onActionAboutQtTriggered);
|
||||
|
||||
m_recentFiles.reset(new RecentFiles(ui->actionRecent));
|
||||
connect(m_recentFiles.get(), &RecentFiles::countChanged, this, [&](int count){
|
||||
ui->actionRecent->setText(tr("%n recent files", nullptr, count));
|
||||
});
|
||||
|
||||
readSettings();
|
||||
m_factory.reset(new ViewerFactory(ui->viewArea, this));
|
||||
const QStringList &viewers = m_factory->viewerNames();
|
||||
|
||||
const QString msg = tr("Available viewers: %1").arg(viewers.join(", "_L1));
|
||||
statusBar()->showMessage(msg);
|
||||
|
||||
auto *menu = new RecentFileMenu(this, m_recentFiles.get());
|
||||
ui->actionRecent->setMenu(menu);
|
||||
connect(menu, &RecentFileMenu::fileOpened, this, &MainWindow::openFile);
|
||||
QWidget *w = ui->mainToolBar->widgetForAction(ui->actionRecent);
|
||||
auto *button = qobject_cast<QToolButton *>(w);
|
||||
if (button)
|
||||
connect(ui->actionRecent, &QAction::triggered, button, &QToolButton::showMenu);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
void MainWindow::onActionOpenTriggered()
|
||||
{
|
||||
QFileDialog fileDialog(this, tr("Open Document"), m_currentDir.absolutePath());
|
||||
while (fileDialog.exec() == QDialog::Accepted
|
||||
&& !openFile(fileDialog.selectedFiles().constFirst())) {
|
||||
}
|
||||
}
|
||||
|
||||
bool MainWindow::openFile(const QString &fileName)
|
||||
{
|
||||
QFile *file = new QFile(fileName);
|
||||
if (!file->exists()) {
|
||||
statusBar()->showMessage(tr("File %1 could not be opened")
|
||||
.arg(QDir::toNativeSeparators(fileName)));
|
||||
delete file;
|
||||
return false;
|
||||
}
|
||||
|
||||
QFileInfo fileInfo(*file);
|
||||
m_currentDir = fileInfo.dir();
|
||||
m_recentFiles->addFile(fileInfo.absoluteFilePath());
|
||||
|
||||
// If a viewer is already open, clean it up and save its settings
|
||||
resetViewer();
|
||||
m_viewer = m_factory->viewer(file);
|
||||
if (!m_viewer) {
|
||||
statusBar()->showMessage(tr("File %1 can't be opened.")
|
||||
.arg(QDir::toNativeSeparators(fileName)));
|
||||
return false;
|
||||
}
|
||||
|
||||
ui->actionPrint->setEnabled(m_viewer->hasContent());
|
||||
connect(m_viewer, &AbstractViewer::printingEnabledChanged, ui->actionPrint, &QAction::setEnabled);
|
||||
connect(ui->actionPrint, &QAction::triggered, m_viewer, &AbstractViewer::print);
|
||||
connect(m_viewer, &AbstractViewer::showMessage, statusBar(), &QStatusBar::showMessage);
|
||||
|
||||
m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget);
|
||||
restoreViewerSettings();
|
||||
ui->scrollArea->setWidget(m_viewer->widget());
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainWindow::onActionAboutTriggered()
|
||||
{
|
||||
const QString viewerNames = m_factory->viewerNames().join(", "_L1);
|
||||
const QString mimeTypes = m_factory->supportedMimeTypes().join(u'\n');
|
||||
QString text = tr("A Widgets application to display and print JSON, "
|
||||
"text and PDF files. Demonstrates various features to use "
|
||||
"in widget applications: Using QSettings, query and save "
|
||||
"user preferences, manage file histories and control cursor "
|
||||
"behavior when hovering over widgets.\n\n"
|
||||
"This version has loaded the following plugins:\n%1\n"
|
||||
"\n\nIt supports the following mime types:\n%2")
|
||||
.arg(viewerNames, mimeTypes);
|
||||
|
||||
if (auto *def = m_factory->defaultViewer())
|
||||
text += tr("\n\nOther mime types will be displayed with %1.").arg(def->viewerName());
|
||||
|
||||
QMessageBox::about(this, tr("About Document Viewer Demo"), text);
|
||||
}
|
||||
|
||||
void MainWindow::onActionAboutQtTriggered()
|
||||
{
|
||||
QMessageBox::aboutQt(this);
|
||||
}
|
||||
|
||||
void MainWindow::readSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
// Restore working directory
|
||||
if (settings.contains(settingsDir))
|
||||
m_currentDir = QDir(settings.value(settingsDir).toString());
|
||||
else
|
||||
m_currentDir = QDir::current();
|
||||
|
||||
// Restore QMainWindow state
|
||||
if (settings.contains(settingsMainWindow)) {
|
||||
QByteArray mainWindowState = settings.value(settingsMainWindow).toByteArray();
|
||||
restoreState(mainWindowState);
|
||||
}
|
||||
|
||||
// Restore recent files
|
||||
m_recentFiles->restoreFromSettings(settings, settingsFiles);
|
||||
}
|
||||
|
||||
void MainWindow::saveSettings() const
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
// Save working directory
|
||||
settings.setValue(settingsDir, m_currentDir.absolutePath());
|
||||
|
||||
// Save QMainWindow state
|
||||
settings.setValue(settingsMainWindow, saveState());
|
||||
|
||||
// Save recent files
|
||||
m_recentFiles->saveSettings(settings, settingsFiles);
|
||||
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
void MainWindow::saveViewerSettings() const
|
||||
{
|
||||
if (!m_viewer)
|
||||
return;
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(settingsViewers);
|
||||
settings.setValue(m_viewer->viewerName(), m_viewer->saveState());
|
||||
settings.endGroup();
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
void MainWindow::resetViewer() const
|
||||
{
|
||||
if (!m_viewer)
|
||||
return;
|
||||
|
||||
saveViewerSettings();
|
||||
m_viewer->cleanup();
|
||||
}
|
||||
|
||||
void MainWindow::restoreViewerSettings()
|
||||
{
|
||||
if (!m_viewer)
|
||||
return;
|
||||
|
||||
QSettings settings;
|
||||
settings.beginGroup(settingsViewers);
|
||||
QByteArray viewerSettings = settings.value(m_viewer->viewerName(), QByteArray()).toByteArray();
|
||||
settings.endGroup();
|
||||
if (!viewerSettings.isEmpty())
|
||||
m_viewer->restoreState(viewerSettings);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef MAINWINDOW_H
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QDir>
|
||||
#include <QStringLiteral>
|
||||
|
||||
class AbstractViewer;
|
||||
class RecentFiles;
|
||||
class ViewerFactory;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
public slots:
|
||||
bool openFile(const QString &fileName);
|
||||
|
||||
private slots:
|
||||
void onActionOpenTriggered();
|
||||
void onActionAboutTriggered();
|
||||
void onActionAboutQtTriggered();
|
||||
|
||||
private:
|
||||
void readSettings();
|
||||
void saveSettings() const;
|
||||
void restoreViewerSettings();
|
||||
void resetViewer() const;
|
||||
void saveViewerSettings() const;
|
||||
|
||||
QDir m_currentDir;
|
||||
AbstractViewer *m_viewer = nullptr;
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
std::unique_ptr<RecentFiles> m_recentFiles;
|
||||
std::unique_ptr<ViewerFactory> m_factory;
|
||||
|
||||
static constexpr QLatin1StringView settingsDir = QLatin1StringView("WorkingDir");
|
||||
static constexpr QLatin1StringView settingsMainWindow = QLatin1StringView("MainWindow");
|
||||
static constexpr QLatin1StringView settingsViewers = QLatin1StringView("Viewers");
|
||||
static constexpr QLatin1StringView settingsFiles = QLatin1StringView("RecentFiles");
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
@@ -0,0 +1,272 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>983</width>
|
||||
<height>602</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Document Viewer Demo</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff>:/demos/documentviewer/images/qt-logo.png</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QWidget" name="viewArea" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::West</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="bookmarkTab">
|
||||
<attribute name="title">
|
||||
<string>Pages</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
<widget class="QWidget" name="pagesTab">
|
||||
<attribute name="title">
|
||||
<string>Bookmarks</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>800</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>798</width>
|
||||
<height>479</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>983</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="qtFileMenu">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionRecent"/>
|
||||
<addaction name="actionPrint"/>
|
||||
<addaction name="actionQuit"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuHelp">
|
||||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="actionAboutQt"/>
|
||||
</widget>
|
||||
<addaction name="qtFileMenu"/>
|
||||
<addaction name="menuHelp"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<widget class="QToolBar" name="mainToolBar">
|
||||
<property name="windowTitle">
|
||||
<string>toolBar</string>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
<attribute name="toolBarBreak">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionRecent"/>
|
||||
<addaction name="actionPrint"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionBack"/>
|
||||
<addaction name="actionForward"/>
|
||||
<addaction name="separator"/>
|
||||
</widget>
|
||||
<action name="actionOpen">
|
||||
<property name="icon">
|
||||
<iconset resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/open.png</normaloff>:/demos/documentviewer/images/open.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+O</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="icon">
|
||||
<iconset theme="help-about" resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/help-about.svgz</normaloff>:/demos/documentviewer/images/help-about.svgz</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>about documentviewer</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show information about the Document Viewer deomo.</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+H</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionForward">
|
||||
<property name="icon">
|
||||
<iconset resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/go-next.svgz</normaloff>:/demos/documentviewer/images/go-next.svgz</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>actionForward</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>One step forward</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Right</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionBack">
|
||||
<property name="icon">
|
||||
<iconset resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/go-previous.svgz</normaloff>:/demos/documentviewer/images/go-previous.svgz</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>actionBack</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>One step back</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Left</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPrint">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-print" resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/print2x.png</normaloff>:/demos/documentviewer/images/print2x.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Print</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Print current file</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+P</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAboutQt">
|
||||
<property name="icon">
|
||||
<iconset resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff>
|
||||
<normalon>:/demos/documentviewer/images/qt-logo.png</normalon>:/demos/documentviewer/images/qt-logo.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About Qt</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Show Qt license information</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+I</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionRecent">
|
||||
<property name="icon">
|
||||
<iconset resource="documentviewer.qrc">
|
||||
<normaloff>:/demos/documentviewer/images/document-open-recent.svgz</normaloff>:/demos/documentviewer/images/document-open-recent.svgz</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Recently opened...</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Meta+R</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionQuit">
|
||||
<property name="icon">
|
||||
<iconset theme="application-exit">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Quit</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Quit the application</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Q</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="documentviewer.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>actionQuit</sender>
|
||||
<signal>triggered()</signal>
|
||||
<receiver>MainWindow</receiver>
|
||||
<slot>close()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>-1</x>
|
||||
<y>-1</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>491</x>
|
||||
<y>300</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "recentfilemenu.h"
|
||||
#include "recentfiles.h"
|
||||
|
||||
#include <QAction>
|
||||
|
||||
RecentFileMenu::RecentFileMenu(QWidget *parent, RecentFiles *recent) :
|
||||
QMenu(parent), m_recentFiles(recent)
|
||||
{
|
||||
Q_ASSERT(recent);
|
||||
connect(m_recentFiles, &RecentFiles::changed, this, &RecentFileMenu::updateList);
|
||||
connect(m_recentFiles, &QObject::destroyed, this, &QObject::deleteLater);
|
||||
updateList();
|
||||
}
|
||||
|
||||
void RecentFileMenu::updateList()
|
||||
{
|
||||
QList<QAction *> acts = actions();
|
||||
qDeleteAll(acts);
|
||||
|
||||
if (m_recentFiles->isEmpty()) {
|
||||
addAction(tr("<no recent files>"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (const QString &fileName : m_recentFiles->recentFiles()) {
|
||||
QAction *action = addAction(fileName);
|
||||
connect(action, &QAction::triggered, this, [&](){
|
||||
QAction *action = qobject_cast<QAction *>(sender());
|
||||
Q_ASSERT(action);
|
||||
emit fileOpened(action->text());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef RECENTFILEMENU_H
|
||||
#define RECENTFILEMENU_H
|
||||
|
||||
#include <QMenu>
|
||||
|
||||
class RecentFiles;
|
||||
|
||||
class RecentFileMenu : public QMenu
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RecentFileMenu(QWidget *parent, RecentFiles *recent);
|
||||
|
||||
signals:
|
||||
void fileOpened(const QString &fileName);
|
||||
|
||||
private slots:
|
||||
void updateList();
|
||||
|
||||
private:
|
||||
RecentFiles *m_recentFiles;
|
||||
};
|
||||
|
||||
#endif // RECENTFILEMENU_H
|
||||
@@ -0,0 +1,128 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "recentfiles.h"
|
||||
#include <QFileInfo>
|
||||
#include <QSettings>
|
||||
|
||||
// Test if file exists and can be opened
|
||||
static bool testFileAccess(const QString &fileName)
|
||||
{
|
||||
return QFileInfo(fileName).isReadable();
|
||||
}
|
||||
|
||||
void RecentFiles::clear()
|
||||
{
|
||||
if (isEmpty())
|
||||
return;
|
||||
|
||||
m_files.clear();
|
||||
emit countChanged(0);
|
||||
}
|
||||
|
||||
void RecentFiles::addFile(const QString &fileName, EmitPolicy policy)
|
||||
{
|
||||
if (!testFileAccess(fileName))
|
||||
return;
|
||||
|
||||
// Remember size, as cleanup can result in a change without size change
|
||||
const qsizetype c = m_files.count();
|
||||
|
||||
// Clean dangling and duplicate files
|
||||
for (qsizetype i = 0; i < m_files.size(); ) {
|
||||
const QString &file = m_files.at(i);
|
||||
if (!testFileAccess(file)) {
|
||||
removeFile(file, RemoveReason::Other);
|
||||
} else if (file == fileName) {
|
||||
removeFile(file, RemoveReason::Duplicate);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Cut tail
|
||||
while (m_files.count() > m_maxFiles)
|
||||
removeFile((m_files.count() - 1), RemoveReason::Other);
|
||||
|
||||
m_files.prepend(fileName);
|
||||
|
||||
switch (policy) {
|
||||
case EmitPolicy::NeverEmit:
|
||||
return;
|
||||
|
||||
case EmitPolicy::EmitWhenChanged:
|
||||
emit changed();
|
||||
|
||||
if (c != m_files.count())
|
||||
emit countChanged(m_files.count());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void RecentFiles::addFiles(const QStringList &files)
|
||||
{
|
||||
if (files.isEmpty())
|
||||
return;
|
||||
|
||||
if (files.count() == 1) {
|
||||
addFile(files.at(0));
|
||||
return;
|
||||
}
|
||||
|
||||
const qsizetype c = m_files.count();
|
||||
|
||||
for (const auto &file : files)
|
||||
addFile(file, EmitPolicy::NeverEmit);
|
||||
|
||||
emit changed();
|
||||
if (m_files.count() != c)
|
||||
emit countChanged(m_files.count());
|
||||
}
|
||||
|
||||
void RecentFiles::removeFile(qsizetype index, RemoveReason reason)
|
||||
{
|
||||
if (index < 0 || index >= m_files.count())
|
||||
return;
|
||||
|
||||
m_files.remove(index);
|
||||
|
||||
// No emit for duplicate removal, add emits changed later.
|
||||
if (reason != RemoveReason::Duplicate)
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void RecentFiles::saveSettings(QSettings &settings, const QString &key) const
|
||||
{
|
||||
settings.beginGroup(key);
|
||||
settings.setValue(s_maxFiles, maxFiles());
|
||||
if (!isEmpty()) {
|
||||
settings.beginWriteArray(s_fileNames, m_files.count());
|
||||
for (int index = 0; index < m_files.count(); ++index) {
|
||||
settings.setArrayIndex(index);
|
||||
settings.setValue(s_file, m_files.at(index));
|
||||
}
|
||||
settings.endArray();
|
||||
}
|
||||
settings.endGroup();
|
||||
}
|
||||
|
||||
bool RecentFiles::restoreFromSettings(QSettings &settings, const QString &key)
|
||||
{
|
||||
settings.beginGroup(key);
|
||||
const qsizetype mxFiles = settings.value(s_maxFiles, maxFiles()).toLongLong();
|
||||
setMaxFiles(mxFiles);
|
||||
m_files.clear(); // clear list without emitting
|
||||
const int numberFiles = settings.beginReadArray(s_fileNames);
|
||||
for (int index = 0; index < numberFiles; ++index) {
|
||||
settings.setArrayIndex(index);
|
||||
const QString absoluteFilePath = settings.value(s_file).toString();
|
||||
addFile(absoluteFilePath, EmitPolicy::NeverEmit);
|
||||
}
|
||||
settings.endArray();
|
||||
settings.endGroup();
|
||||
if (!m_files.isEmpty())
|
||||
emit changed();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef RECENTFILES_H
|
||||
#define RECENTFILES_H
|
||||
|
||||
#include <QMenu>
|
||||
#include <QFile>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class RecentFiles : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum class RemoveReason {
|
||||
Other,
|
||||
Duplicate
|
||||
};
|
||||
Q_ENUM(RemoveReason)
|
||||
|
||||
using QObject::QObject;
|
||||
|
||||
// Access to QStringList member functions
|
||||
const QStringList recentFiles() const {return m_files; }
|
||||
bool isEmpty() const { return m_files.isEmpty(); }
|
||||
|
||||
// Properties
|
||||
qsizetype maxFiles() const { return m_maxFiles; }
|
||||
void setMaxFiles(qsizetype maxFiles) { m_maxFiles = maxFiles; }
|
||||
|
||||
public slots:
|
||||
void addFile(const QString &fileName) { addFile(fileName, EmitPolicy::EmitWhenChanged); }
|
||||
void addFiles(const QStringList &fileNames);
|
||||
void removeFile(const QString &fileName) { removeFile(m_files.indexOf(fileName)); }
|
||||
void removeFile(qsizetype index) {removeFile(index, RemoveReason::Other); }
|
||||
void saveSettings(QSettings &settings, const QString &key) const;
|
||||
bool restoreFromSettings(QSettings &settings, const QString &key);
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
void countChanged(int count);
|
||||
void changed();
|
||||
|
||||
private:
|
||||
// Private removers with reason
|
||||
void removeFile(qsizetype index, RemoveReason reason);
|
||||
void removeFile(const QString &fileName, RemoveReason reason) {removeFile(m_files.indexOf(fileName), reason); }
|
||||
|
||||
// Private adder with emit policy
|
||||
enum class EmitPolicy {
|
||||
EmitWhenChanged,
|
||||
NeverEmit
|
||||
};
|
||||
|
||||
void addFile(const QString &fileName, EmitPolicy policy);
|
||||
|
||||
qsizetype m_maxFiles = 10;
|
||||
|
||||
QStringList m_files;
|
||||
|
||||
// Constexprs for settings
|
||||
static constexpr QLatin1StringView s_maxFiles = QLatin1StringView("maxFiles");
|
||||
static constexpr QLatin1StringView s_openMode = QLatin1StringView("openMode");
|
||||
static constexpr QLatin1StringView s_fileNames = QLatin1StringView("fileNames");
|
||||
static constexpr QLatin1StringView s_file = QLatin1StringView("file");
|
||||
};
|
||||
|
||||
#endif // RECENTFILES_H
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "abstractviewer.h"
|
||||
#include "viewerfactory.h"
|
||||
#include "viewerinterfaces.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QWidget>
|
||||
|
||||
#include <QDir>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
#include <QPluginLoader>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
ViewerFactory::ViewerFactory(QWidget *displayWidget, QMainWindow *mainWindow, DefaultPolicy policy)
|
||||
: m_defaultPolicy(policy),
|
||||
m_displayWidget(displayWidget),
|
||||
m_mainWindow(mainWindow)
|
||||
{
|
||||
Q_ASSERT(m_displayWidget);
|
||||
Q_ASSERT(m_mainWindow);
|
||||
loadViewerPlugins();
|
||||
}
|
||||
|
||||
ViewerFactory::~ViewerFactory()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
AbstractViewer *ViewerFactory::viewer(QFile *file) const
|
||||
{
|
||||
Q_ASSERT(file);
|
||||
|
||||
const QFileInfo info(*file);
|
||||
QMimeDatabase db;
|
||||
const auto mimeType = db.mimeTypeForFile(info);
|
||||
|
||||
AbstractViewer *viewer = ViewerFactory::viewer(mimeType);
|
||||
if (!viewer) {
|
||||
qWarning() << "Mime type" << mimeType.name() << "not supported.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
viewer->init(file, m_displayWidget, m_mainWindow);
|
||||
return viewer;
|
||||
}
|
||||
|
||||
void ViewerFactory::loadViewerPlugins()
|
||||
{
|
||||
if (!m_viewers.isEmpty())
|
||||
return;
|
||||
|
||||
// Load static plugins
|
||||
const QObjectList &staticPlugins = QPluginLoader::staticInstances();
|
||||
for (auto *plugin : staticPlugins)
|
||||
addViewer(plugin);
|
||||
|
||||
// Load shared plugins
|
||||
QDir pluginsDir = QDir(QApplication::applicationDirPath());
|
||||
|
||||
#if defined(Q_OS_WINDOWS)
|
||||
pluginsDir.cd("app"_L1);
|
||||
#elif defined(Q_OS_DARWIN)
|
||||
if (pluginsDir.dirName() == "MacOS"_L1) {
|
||||
pluginsDir.cdUp();
|
||||
pluginsDir.cdUp();
|
||||
pluginsDir.cdUp();
|
||||
}
|
||||
#endif
|
||||
const auto entryList = pluginsDir.entryList(QDir::Files);
|
||||
for (const QString &fileName : entryList) {
|
||||
QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
|
||||
QObject *plugin = loader.instance();
|
||||
if (plugin)
|
||||
addViewer(plugin);
|
||||
#if 0
|
||||
else
|
||||
qDebug() << loader.errorString();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerFactory::unload()
|
||||
{
|
||||
qDeleteAll(viewers());
|
||||
m_viewers.clear();
|
||||
}
|
||||
|
||||
void ViewerFactory::addViewer(QObject *viewerObject)
|
||||
{
|
||||
auto *interface = qobject_cast<ViewerInterface *>(viewerObject);
|
||||
if (!interface)
|
||||
return;
|
||||
|
||||
// Set custom default viewer
|
||||
if (interface->viewer()->isDefaultViewer())
|
||||
m_defaultViewer = interface->viewer();
|
||||
|
||||
m_viewers.insert(interface->viewerName(), interface->viewer());
|
||||
}
|
||||
|
||||
QStringList ViewerFactory::viewerNames(bool showDefault) const
|
||||
{
|
||||
if (!showDefault)
|
||||
return m_viewers.keys();
|
||||
|
||||
QStringList list;
|
||||
for (auto it = m_viewers.constBegin(); it != m_viewers.constEnd(); ++it) {
|
||||
QString name = it.key();
|
||||
if ((m_defaultViewer && it.value()->isDefaultViewer())
|
||||
|| (!m_defaultViewer && it.key() == "TxtViewer"_L1)) {
|
||||
name += "(default)"_L1;
|
||||
}
|
||||
list.append(name);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
ViewerFactory::ViewerList ViewerFactory::viewers() const
|
||||
{
|
||||
return m_viewers.values();
|
||||
}
|
||||
|
||||
AbstractViewer *ViewerFactory::findViewer(const QString &viewerName) const
|
||||
{
|
||||
const ViewerList &viewerList = viewers();
|
||||
for (auto *viewer : viewerList) {
|
||||
if (viewer->viewerName() == viewerName)
|
||||
return viewer;
|
||||
}
|
||||
qWarning() << "Plugin" << viewerName << "not loaded.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AbstractViewer *ViewerFactory::viewer(const QMimeType &mimeType) const
|
||||
{
|
||||
const ViewerList &viewerList = viewers();
|
||||
|
||||
for (AbstractViewer *viewer : viewerList) {
|
||||
for (const QString &type : viewer->supportedMimeTypes()) {
|
||||
if (mimeType.inherits(type))
|
||||
return viewer;
|
||||
}
|
||||
}
|
||||
|
||||
AbstractViewer *viewer = defaultViewer();
|
||||
|
||||
if (m_defaultWarning) {
|
||||
QMessageBox mbox;
|
||||
mbox.setIcon(QMessageBox::Warning);
|
||||
mbox.setText(QObject::tr("Mime type %1 not supported. Falling back to %2.")
|
||||
.arg(mimeType.name(), viewer->viewerName()));
|
||||
mbox.setStandardButtons(QMessageBox::Ok);
|
||||
QTimer::singleShot(8000, &mbox, [&mbox](){ mbox.close(); });
|
||||
mbox.exec();
|
||||
}
|
||||
|
||||
return viewer;
|
||||
}
|
||||
|
||||
AbstractViewer *ViewerFactory::defaultViewer() const
|
||||
{
|
||||
switch (m_defaultPolicy) {
|
||||
case DefaultPolicy::NeverDefault:
|
||||
return nullptr;
|
||||
case DefaultPolicy::DefaultToCustomViewer:
|
||||
return m_defaultViewer ? m_defaultViewer : findViewer("TxtViewer"_L1);
|
||||
case DefaultPolicy::DefaultToTxtViewer:
|
||||
return findViewer("TxtViewer"_L1);
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
QStringList ViewerFactory::supportedMimeTypes() const
|
||||
{
|
||||
static QStringList mimeTypes;
|
||||
if (!mimeTypes.isEmpty())
|
||||
return mimeTypes;
|
||||
|
||||
const ViewerList &viewerList = viewers();
|
||||
for (auto viewer : viewerList)
|
||||
mimeTypes.append(viewer->supportedMimeTypes());
|
||||
return mimeTypes;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef VIEWERFACTORY_H
|
||||
#define VIEWERFACTORY_H
|
||||
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMimeType;
|
||||
class QWidget;
|
||||
class QMainWindow;
|
||||
class QFile;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class AbstractViewer;
|
||||
|
||||
class ViewerFactory
|
||||
{
|
||||
public:
|
||||
enum class DefaultPolicy {
|
||||
NeverDefault,
|
||||
DefaultToTxtViewer,
|
||||
DefaultToCustomViewer
|
||||
};
|
||||
|
||||
explicit ViewerFactory(QWidget *displayWidget, QMainWindow *mainWindow,
|
||||
DefaultPolicy policy = DefaultPolicy::DefaultToTxtViewer);
|
||||
~ViewerFactory();
|
||||
|
||||
DefaultPolicy defaultPolicy() const { return m_defaultPolicy; }
|
||||
void setDefaultPolicy(DefaultPolicy policy) { m_defaultPolicy = policy; }
|
||||
bool defaultWarning() const { return m_defaultWarning; }
|
||||
void setDefaultWarning(bool on) { m_defaultWarning = on; }
|
||||
|
||||
AbstractViewer *viewer(QFile *file) const;
|
||||
|
||||
|
||||
using ViewerMap = QMap<QString, AbstractViewer *>;
|
||||
using ViewerList = QList<AbstractViewer *>;
|
||||
QStringList viewerNames(bool showDefault = false) const;
|
||||
ViewerList viewers() const;
|
||||
AbstractViewer *findViewer(const QString &viewerName) const;
|
||||
AbstractViewer *defaultViewer() const;
|
||||
QStringList supportedMimeTypes() const;
|
||||
|
||||
private:
|
||||
DefaultPolicy m_defaultPolicy;
|
||||
QWidget *m_displayWidget;
|
||||
QMainWindow *m_mainWindow;
|
||||
ViewerMap m_viewers;
|
||||
AbstractViewer *m_defaultViewer = nullptr;
|
||||
bool m_defaultWarning = true;
|
||||
|
||||
void loadViewerPlugins();
|
||||
void addViewer(QObject *viewerObject);
|
||||
AbstractViewer *viewer(const QMimeType &type) const;
|
||||
void unload();
|
||||
};
|
||||
|
||||
#endif // VIEWERFACTORY_H
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef VIEWERINTERFACES_H
|
||||
#define VIEWERINTERFACES_H
|
||||
|
||||
#include "abstractviewer.h"
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPrinter;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class ViewerInterface : public AbstractViewer
|
||||
{
|
||||
public:
|
||||
virtual ~ViewerInterface() = default;
|
||||
virtual QString viewerName() const override = 0;
|
||||
virtual QByteArray saveState() const override = 0;
|
||||
virtual bool restoreState(QByteArray &) override = 0;
|
||||
virtual bool supportsOverview() const override = 0;
|
||||
virtual bool hasContent() const override = 0;
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
virtual void printDocument(QPrinter *) const override = 0;
|
||||
virtual QStringList supportedMimeTypes() const override = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
#define ViewerInterface_iid "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0"
|
||||
Q_DECLARE_INTERFACE(ViewerInterface, ViewerInterface_iid)
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif //VIEWERINTERFACES_H
|
||||
@@ -0,0 +1,67 @@
|
||||
[
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/abstractviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/abstractviewer.dir/abstractviewer_autogen/mocs_compilation.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/build/app/abstractviewer_autogen/mocs_compilation.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/build/app/abstractviewer_autogen/mocs_compilation.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/abstractviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/abstractviewer.dir/abstractviewer.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/app/abstractviewer.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/app/abstractviewer.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/documentviewer_autogen/mocs_compilation.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/mocs_compilation.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/mocs_compilation.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/main.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/app/main.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/app/main.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/mainwindow.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/app/mainwindow.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/app/mainwindow.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/viewerfactory.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/app/viewerfactory.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/app/viewerfactory.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/recentfiles.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/app/recentfiles.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/app/recentfiles.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/recentfilemenu.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/app/recentfilemenu.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/app/recentfilemenu.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/app",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -I/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/include -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -o CMakeFiles/documentviewer.dir/documentviewer_autogen/EWIEGA46WW/qrc_documentviewer.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/EWIEGA46WW/qrc_documentviewer.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/build/app/documentviewer_autogen/EWIEGA46WW/qrc_documentviewer.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/plugins/jsonviewer",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DEPRECATED_WARNINGS -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PLUGIN -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -Djsonviewer_EXPORTS -I/home/talksik/code/qt-tinkering/documentviewer/build/plugins/jsonviewer/jsonviewer_autogen/include -I/home/talksik/code/qt-tinkering/documentviewer/plugins/jsonviewer/../../app -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -fPIC -o CMakeFiles/jsonviewer.dir/jsonviewer_autogen/mocs_compilation.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/build/plugins/jsonviewer/jsonviewer_autogen/mocs_compilation.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/build/plugins/jsonviewer/jsonviewer_autogen/mocs_compilation.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/plugins/jsonviewer",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DEPRECATED_WARNINGS -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PLUGIN -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -Djsonviewer_EXPORTS -I/home/talksik/code/qt-tinkering/documentviewer/build/plugins/jsonviewer/jsonviewer_autogen/include -I/home/talksik/code/qt-tinkering/documentviewer/plugins/jsonviewer/../../app -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -fPIC -o CMakeFiles/jsonviewer.dir/jsonviewer.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/plugins/jsonviewer/jsonviewer.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/plugins/jsonviewer/jsonviewer.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/plugins/txtviewer",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DEPRECATED_WARNINGS -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PLUGIN -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -Dtxtviewer_EXPORTS -I/home/talksik/code/qt-tinkering/documentviewer/build/plugins/txtviewer/txtviewer_autogen/include -I/home/talksik/code/qt-tinkering/documentviewer/plugins/txtviewer/../../app -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -fPIC -o CMakeFiles/txtviewer.dir/txtviewer_autogen/mocs_compilation.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/build/plugins/txtviewer/txtviewer_autogen/mocs_compilation.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/build/plugins/txtviewer/txtviewer_autogen/mocs_compilation.cpp"
|
||||
},
|
||||
{
|
||||
"directory": "/home/talksik/code/qt-tinkering/documentviewer/build/plugins/txtviewer",
|
||||
"command": "/usr/bin/c++ -DQT_CORE_LIB -DQT_DEPRECATED_WARNINGS -DQT_DOCUMENTVIEWER_PRINTSUPPORT -DQT_GUI_LIB -DQT_NO_CAST_FROM_ASCII -DQT_PLUGIN -DQT_PRINTSUPPORT_LIB -DQT_WIDGETS_LIB -Dtxtviewer_EXPORTS -I/home/talksik/code/qt-tinkering/documentviewer/build/plugins/txtviewer/txtviewer_autogen/include -I/home/talksik/code/qt-tinkering/documentviewer/plugins/txtviewer/../../app -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtCore -isystem /home/talksik/Qt/6.6.1/gcc_64/include -isystem /home/talksik/Qt/6.6.1/gcc_64/mkspecs/linux-g++ -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtGui -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtWidgets -isystem /home/talksik/Qt/6.6.1/gcc_64/include/QtPrintSupport -g -fPIC -fPIC -o CMakeFiles/txtviewer.dir/txtviewer.cpp.o -c /home/talksik/code/qt-tinkering/documentviewer/plugins/txtviewer/txtviewer.cpp",
|
||||
"file": "/home/talksik/code/qt-tinkering/documentviewer/plugins/txtviewer/txtviewer.cpp"
|
||||
}
|
||||
]
|
||||
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,288 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\title Document Viewer
|
||||
\ingroup examples-widgets
|
||||
\example demos/documentviewer
|
||||
\examplecategory {Application Examples}
|
||||
\meta {tag} {demo,widgets,mainwindow}
|
||||
\brief A Widgets application to display and print JSON, text, and PDF files.
|
||||
|
||||
|
||||
\e{Document Viewer} demonstrates how to use a QMainWindow with static
|
||||
and dynamic toolbars, menus, and actions. Additionally, it demonstrates
|
||||
the following features in widget-based applications:
|
||||
|
||||
\list
|
||||
\li Using QSettings to query and save user preferences,
|
||||
and managing previously opened file history.
|
||||
\li Controlling cursor behavior when hovering over widgets.
|
||||
\li Creating dynamically loaded plugins.
|
||||
\endlist
|
||||
|
||||
\image documentviewer_open.png
|
||||
|
||||
|
||||
\section1 Creating an application and the main window
|
||||
|
||||
The application and its main window is constructed in \c main.cpp.
|
||||
The main() function uses QCommandLineParser to process command line
|
||||
arguments -- \e help, \e version, and an optional positional
|
||||
argument, \e file. If the user provided a path to a file when
|
||||
launching the application, the main window opens it:
|
||||
|
||||
\quotefromfile demos/documentviewer/app/main.cpp
|
||||
\skipto int main
|
||||
\printuntil exec
|
||||
\printline }
|
||||
|
||||
|
||||
\section1 MainWindow class
|
||||
|
||||
The \c MainWindow class provides an application screen with menus,
|
||||
actions, and a toolbar. It can open a file, automatically detecting its
|
||||
content type. It also maintains a list of previously opened files, using
|
||||
QSettings to store and reload settings when launched. The MainWindow
|
||||
creates a suitable \e viewer for the opened file, based on its content type,
|
||||
and provides support for printing a document.
|
||||
|
||||
MainWindow's constructor initializes the user interface created in Qt
|
||||
Designer. The \c mainwindow.ui file provides a QTabWidget on the left,
|
||||
showing bookmarks and thumbnails. On the right, there is a QScrollArea for
|
||||
viewing file content.
|
||||
|
||||
|
||||
\section1 ViewerFactory class
|
||||
|
||||
The \c ViewerFactory class manages viewers for known file types. These viewers
|
||||
are implemented as plugins. When an instance of a ViewerFactory is created,
|
||||
pointers to the view area and the main window are passed to the constructor:
|
||||
|
||||
\code
|
||||
m_factory.reset(new ViewerFactory(ui->viewArea, this));
|
||||
\endcode
|
||||
|
||||
ViewerFactory loads all available plugins on construction. It provides
|
||||
a public API to query the loaded plugins, their names, and supported MIME
|
||||
types:
|
||||
|
||||
\quotefromfile demos/documentviewer/app/viewerfactory.h
|
||||
\skipto ViewerList
|
||||
\printuntil QStringList supportedMimeTypes() const;
|
||||
|
||||
The \c viewer() function returns a pointer to the plugin suitable to open
|
||||
the QFile passed as an argument:
|
||||
|
||||
\code
|
||||
m_viewer = m_factory->viewer(file);
|
||||
\endcode
|
||||
|
||||
If the application settings contain a section for the viewer, it's passed
|
||||
to the viewer's virtual \c restoreState() function:
|
||||
|
||||
\quotefromfile demos/documentviewer/app/mainwindow.cpp
|
||||
\skipto MainWindow::restoreViewerSettings
|
||||
\printuntil restoreState
|
||||
\printline }
|
||||
|
||||
Then, the standard UI assets are passed to the viewer and the main scroll
|
||||
area is set to show the viewer's display widget:
|
||||
|
||||
\quotefromfile demos/documentviewer/app/mainwindow.cpp
|
||||
\skipuntil bool MainWindow::openFile
|
||||
\skipto m_viewer->initViewer
|
||||
\printuntil }
|
||||
|
||||
|
||||
\section1 AbstractViewer class
|
||||
|
||||
\c AbstractViewer provides a generalized API to view, save, and print a
|
||||
document. Properties of both the document and the viewer can be queried:
|
||||
|
||||
\list
|
||||
\li Does the document have content?
|
||||
\li Has it been modified?
|
||||
\li Is an overview (thumbnails or bookmarks) supported?
|
||||
\endlist
|
||||
|
||||
AbstractViewer provides protected methods for derived classes to create
|
||||
actions and menus on the main window. In order to display these
|
||||
assets on the main window, they are parented to it. AbstractViewer is
|
||||
responsible for removing and destroying the UI assets it creates. It
|
||||
inherits from QObject to implement signals and slots.
|
||||
|
||||
\section2 Signals
|
||||
|
||||
\c {void uiInitialized();}
|
||||
|
||||
This signal is emitted after a viewer receives all necessary information
|
||||
about UI assets on the main window.
|
||||
|
||||
\c {void printingEnabledChanged(bool enabled);}
|
||||
|
||||
This signal is emitted when document printing is either enabled or
|
||||
disabled. This happens after a new document was successfully loaded,
|
||||
or, for example, all content was removed.
|
||||
|
||||
\c {void printStatusChanged(AbstractViewer::PrintStatus status);}
|
||||
|
||||
After starting the printing process, this signal notifies about changes in
|
||||
its progress.
|
||||
|
||||
\c {void documentLoaded(const QString &fileName);}
|
||||
|
||||
This signal notifies the application that a document was successfully
|
||||
loaded.
|
||||
|
||||
|
||||
\section1 TxtViewer class
|
||||
|
||||
\c TxtViewer is a simple text viewer, inheriting from AbstractViewer.
|
||||
It supports editing text files, copy/cut and paste, printing, and
|
||||
saving changes.
|
||||
|
||||
|
||||
\section1 JsonViewer class
|
||||
|
||||
\c JsonViewer displays a JSON file in a QTreeView. Internally, it loads
|
||||
the contents of a file into a QJsonDocument and uses it to populate a
|
||||
custom tree model with \c JsonItemModel.
|
||||
|
||||
The JSON viewer plugin demonstrates how to implement a custom item model
|
||||
inherited from QAbstractItemModel. The \c JsonTreeItem class provides a
|
||||
basic API for manipulating JSON data and propagating it back to the
|
||||
underlying QJsonDocument.
|
||||
|
||||
JsonViewer uses the top-level objects of the document as bookmarks for
|
||||
navigation. Other nodes (keys and values) can be added as additional
|
||||
bookmarks, or removed from the bookmark list. A QLineEdit is used as a
|
||||
search field to navigate through the JSON tree.
|
||||
|
||||
|
||||
\section1 PdfViewer class
|
||||
|
||||
The \c PdfViewer class (and plugin) is a fork of the \l {PDF Viewer
|
||||
Widget Example}. It demonstrates the use of QScroller to smoothly
|
||||
flick through a document.
|
||||
|
||||
|
||||
\section1 Other relevant classes
|
||||
|
||||
\section2 HoverWatcher class
|
||||
|
||||
The \c HoverWatcher class sets an override cursor when hovering the
|
||||
mouse over a widget, restoring it upon departure. To prevent multiple
|
||||
HoverWatcher instances being created for the same widget, it is
|
||||
implemented as a singleton per widget.
|
||||
|
||||
HoverWatcher inherits from QObject and takes the QWidget it watches
|
||||
as the instance's parent. It installs an event filter to intercept hover
|
||||
events without consuming them:
|
||||
|
||||
\quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.cpp
|
||||
\skipto HoverWatcher::HoverWatcher
|
||||
\printuntil }
|
||||
|
||||
The \c HoverAction enum lists the actions that HoverWatcher reacts to:
|
||||
|
||||
\quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
|
||||
\skipto enum HoverAction
|
||||
\printuntil };
|
||||
|
||||
Static functions create watchers, check their existence for a specific
|
||||
QWidget, or dismiss a watcher:
|
||||
|
||||
\quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
|
||||
\skipto static HoverWatcher
|
||||
\printuntil static void dismiss
|
||||
|
||||
A cursor shape can be set or unset for each HoverAction. If there is
|
||||
no associated cursor shape, the application's override cursor is
|
||||
restored when the action is triggered.
|
||||
|
||||
\quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
|
||||
\skipto public slots
|
||||
\printuntil void unSetCursorShape
|
||||
|
||||
The \c mouseButtons property holds the mouse buttons to consider for a
|
||||
\c MousePress action:
|
||||
|
||||
\quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
|
||||
\skipuntil public slots
|
||||
\skipto setMouseButtons
|
||||
\printuntil setMouseButton(
|
||||
|
||||
Action-specific signals are emitted after processing an action:
|
||||
|
||||
\quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
|
||||
\skipto signals
|
||||
\printuntil left();
|
||||
|
||||
A general signal is emitted which passes the processed action as an
|
||||
argument:
|
||||
|
||||
\code
|
||||
void hoverAction(HoverAction action);
|
||||
\endcode
|
||||
|
||||
\section2 RecentFiles class
|
||||
|
||||
\c RecentFiles is a QStringList that is specialized to manage a list of
|
||||
recently opened files.
|
||||
|
||||
RecentFiles has slots to add either a single file or multiple files in one
|
||||
go. An entry is added to the list of recent files if the path points to a
|
||||
file that exists and can be opened. If a file is already in the list, it
|
||||
is removed from its original position and added to the top.
|
||||
|
||||
\quotefromfile demos/documentviewer/app/recentfiles.h
|
||||
\skipto public slots
|
||||
\printuntil addFiles
|
||||
|
||||
Files are removed from the list either by name or by index:
|
||||
|
||||
\quotefromfile demos/documentviewer/app/recentfiles.h
|
||||
\skipuntil public slots
|
||||
\skipto removeFile
|
||||
\printuntil qsizetype index
|
||||
|
||||
Slots that implement saving and restoring from QSettings:
|
||||
|
||||
\quotefromfile demos/documentviewer/app/recentfiles.h
|
||||
\skipuntil public slots
|
||||
\skipto saveSettings
|
||||
\printuntil restoreFromSettings
|
||||
|
||||
When restoring settings, nonexistent files are ignored. The \c maxFiles
|
||||
property holds the maximum amount of recent files to store (default is
|
||||
10).
|
||||
|
||||
\code
|
||||
qsizetype maxFiles();
|
||||
void setMaxFiles(qsizetype maxFiles);
|
||||
\endcode
|
||||
|
||||
\c {RecentFiles} verifies that a file can be read before
|
||||
accepting it.
|
||||
|
||||
\section2 RecentFileMenu class
|
||||
|
||||
\c {RecentFileMenu} is a QMenu, specialized to display a
|
||||
\l{RecentFiles class}{RecentFiles} object as a submenu.
|
||||
|
||||
Its constructor takes a pointer to a parent QObject and a pointer to a
|
||||
RecentFiles object, the content of which it will visualize.
|
||||
Its \c fileOpened() signal, triggered when the user selects a recent file
|
||||
from the list, passes the absolute path to the file as an argument.
|
||||
|
||||
\note \c {RecentFileMenu} is destroyed either by its parent widget, or by the
|
||||
\c {RecentFiles} object passed to its constructor.
|
||||
|
||||
\quotefromfile demos/documentviewer/app/recentfilemenu.h
|
||||
\skipto class RecentFileMenu
|
||||
\printuntil void fileOpened
|
||||
\dots
|
||||
\skipuntil RecentFiles
|
||||
\printline }
|
||||
*/
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
\example demos/documentviewer/plugins/txtviewer
|
||||
\title Qt Widgets - Text Viewer Plugin Example
|
||||
\examplecategory {Graphics & Multimedia}
|
||||
\ingroup examples-mainwindow
|
||||
|
||||
\brief A widget example with menus, toolbars and a status bar.
|
||||
|
||||
The Text Viewer example is a text editor built around QPlainTextEdit,
|
||||
in the form of a plugin for a general-purpose \l {Document Viewer}.
|
||||
|
||||
\image txtviewer_screenshot.png
|
||||
|
||||
All of the code for the Text Viewer example is in the \c TxtViewer class,
|
||||
which inherits \c AbstractViewer. \c AbstractViewer provides the framework
|
||||
for interaction between a viewer and the main window. The application provides
|
||||
\uicontrol{File}, \uicontrol{Edit}, and \uicontrol{Help} entries in the menu
|
||||
bar.
|
||||
|
||||
The status bar at the bottom of the main window shows a message provided by
|
||||
the application to its user.
|
||||
|
||||
Recently opened files are shown in the \uicontrol{File} menu.
|
||||
The example can only load one file at a time.
|
||||
|
||||
\section1 Class Definition
|
||||
|
||||
\snippet demos/documentviewer/plugins/txtviewer/txtviewer.h interfacing
|
||||
|
||||
The class definition starts with the \c Q_OBJECT macro, which handles
|
||||
signals and slots. It is followed by the \c Q_PLUGIN_METADATA and
|
||||
\c Q_INTERFACES macros which are necessary to register the plugin.
|
||||
|
||||
The class inherits from \c ViewerInterface, which inherits from
|
||||
\c AbstractViewer. The \c ViewerInterface class is used to provide an interface
|
||||
between the main window application and the plugin.
|
||||
|
||||
\c QPluginLoader also requires the file txtviewer.json, which has to contain
|
||||
the plugin's key:
|
||||
\code
|
||||
{ "Keys": [ "txtviewer" ] }
|
||||
\endcode
|
||||
|
||||
\snippet demos/documentviewer/plugins/txtviewer/txtviewer.h classDefinition
|
||||
|
||||
The class defines no constructor, which means that only a standard constructor
|
||||
without arguments is available. All other functions, including the destructor,
|
||||
re-implement virtual functions of \c ViewerInterface. They are used to exchange
|
||||
data, information, and instructions with the main application.
|
||||
|
||||
No functionality is implemented to save and restore settings.
|
||||
The \c supportsOverview function always returns \c false, which tells the main
|
||||
application that no window for thumbnail navigation has to be displayed.
|
||||
|
||||
\section1 TxtViewer Class Implementation
|
||||
|
||||
\snippet demos/documentviewer/plugins/txtviewer/txtviewer.cpp init
|
||||
|
||||
We start by including the header files necessary to access all classes used by
|
||||
\c TxtViewer. We also include \c txtviewer.h.
|
||||
|
||||
\c QPrinter and \c QPrintDialog are only included if print support is enabled
|
||||
on the compilation system.
|
||||
|
||||
You might wonder why we don't include these headers in \c mainwindow.h and
|
||||
be done with it. The reason is that including multiple large headers from another
|
||||
header file can rapidly degrade performance. Here, it wouldn't do any harm,
|
||||
but it's still generally a good idea to include only the header files that are
|
||||
strictly necessary from another header file.
|
||||
|
||||
The implementation starts with an empty destructor. It could be completely omitted.
|
||||
It's good practice to implement it empty in order to point out to code readers that
|
||||
nothing needs to be done in the destructor.
|
||||
|
||||
The destructor is followed by an initialization function, taking three arguments:
|
||||
\list
|
||||
\li \c file, the pointer to the file to be opened and displayed.
|
||||
\li \c parent, pointing to the \c QWidget inside which the editor shall be placed.
|
||||
\li \c mainWindow, pointing to the application's main window, where menus and menu bars
|
||||
are handled.
|
||||
\endlist
|
||||
The function calls the base init function of \c AbstractViwer.
|
||||
A new QPlainTextEdit widget is created, which will display the file's contents.
|
||||
Then, \c TxtViewer's setup function is connected to the base class' uiInitialized signal.
|
||||
|
||||
The next function returns the list of mime types, which the text viewer supports.
|
||||
Only plain text is supported.
|
||||
|
||||
The last initialization function adds viewer specific UI components like menus,
|
||||
icons, buttons, and tooltips. It uses functionality provided by \c AbstractViewer
|
||||
to make sure that these components are removed from the application's main window,
|
||||
once another file is displayed with another viewer plugin.
|
||||
|
||||
\snippet demos/documentviewer/plugins/txtviewer/txtviewer.cpp open
|
||||
|
||||
\c openFile opens a file, transfers its contents into the QPlainTextEdit, and prints
|
||||
a status message for the user, depending on whether or not the opening was successful.
|
||||
|
||||
\snippet demos/documentviewer/plugins/txtviewer/txtviewer.cpp infoPrintAndSave
|
||||
|
||||
The next re-implemented function tells the main application whether or not the viewer
|
||||
plugin is actually displaying content.
|
||||
|
||||
If printing is supported on the compiling system, the next section implements it.
|
||||
|
||||
The last two re-implementations provide functionality to save the current file or
|
||||
to save it under a new name.
|
||||
*/
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
add_subdirectory(jsonviewer)
|
||||
add_subdirectory(txtviewer)
|
||||
|
||||
if(TARGET Qt6::PdfWidgets)
|
||||
add_subdirectory(pdfviewer)
|
||||
endif()
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_add_plugin(jsonviewer
|
||||
CLASS_NAME JsonViewer
|
||||
jsonviewer.cpp jsonviewer.h
|
||||
)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets
|
||||
OPTIONAL_COMPONENTS PrintSupport)
|
||||
|
||||
set_target_properties(jsonviewer PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/app"
|
||||
)
|
||||
|
||||
target_include_directories(jsonviewer PRIVATE
|
||||
../../app
|
||||
)
|
||||
|
||||
target_link_libraries(jsonviewer PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
abstractviewer
|
||||
)
|
||||
|
||||
if(TARGET Qt6::PrintSupport)
|
||||
target_link_libraries(jsonviewer PRIVATE Qt6::PrintSupport)
|
||||
endif()
|
||||
|
||||
install(TARGETS jsonviewer
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,525 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "jsonviewer.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QMenu>
|
||||
#include <QToolBar>
|
||||
#include <QTreeView>
|
||||
|
||||
#include <QDrag>
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QMimeData>
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
#include <QPrinter>
|
||||
#include <QPainter>
|
||||
#endif
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
JsonViewer::JsonViewer()
|
||||
{
|
||||
connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi);
|
||||
}
|
||||
|
||||
void JsonViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
|
||||
{
|
||||
AbstractViewer::init(file, new QTreeView(parent), mainWindow);
|
||||
m_tree = qobject_cast<QTreeView *>(widget());
|
||||
}
|
||||
|
||||
JsonViewer::~JsonViewer()
|
||||
{
|
||||
delete m_toplevel;
|
||||
}
|
||||
|
||||
QStringList JsonViewer::supportedMimeTypes() const
|
||||
{
|
||||
return {"application/json"_L1};
|
||||
}
|
||||
|
||||
void JsonViewer::setupJsonUi()
|
||||
{
|
||||
// Build Menus and toolbars
|
||||
QMenu *menu = addMenu(tr("Json"));
|
||||
QToolBar *tb = addToolBar(tr("Json Actions"));
|
||||
|
||||
const QIcon zoomInIcon = QIcon::fromTheme("zoom-in"_L1);
|
||||
QAction *a = menu->addAction(zoomInIcon, tr("&+Expand all"), m_tree, &QTreeView::expandAll);
|
||||
tb->addAction(a);
|
||||
a->setPriority(QAction::LowPriority);
|
||||
a->setShortcut(QKeySequence::New);
|
||||
|
||||
const QIcon zoomOutIcon = QIcon::fromTheme("zoom-out"_L1);
|
||||
a = menu->addAction(zoomOutIcon, tr("&-Collapse all"), m_tree, &QTreeView::collapseAll);
|
||||
tb->addAction(a);
|
||||
a->setPriority(QAction::LowPriority);
|
||||
a->setShortcut(QKeySequence::New);
|
||||
|
||||
if (!m_searchKey)
|
||||
m_searchKey = new QLineEdit(tb);
|
||||
|
||||
auto *label = new QLabel(tb);
|
||||
const QPixmap magnifier = QPixmap(":/icons/images/magnifier.png"_L1).scaled(QSize(28, 28));
|
||||
label->setPixmap(magnifier);
|
||||
tb->addWidget(label);
|
||||
tb->addWidget(m_searchKey);
|
||||
connect(m_searchKey, &QLineEdit::textEdited, m_tree, &QTreeView::keyboardSearch);
|
||||
|
||||
openJsonFile();
|
||||
|
||||
if (m_root.isEmpty())
|
||||
return;
|
||||
|
||||
// Populate bookmarks with toplevel
|
||||
m_uiAssets.tabs->clear();
|
||||
m_toplevel = new QListWidget(m_uiAssets.tabs);
|
||||
m_uiAssets.tabs->addTab(m_toplevel, tr("Bookmarks"));
|
||||
qRegisterMetaType<QModelIndex>();
|
||||
for (int i = 0; i < m_tree->model()->rowCount(); ++i) {
|
||||
const auto &index = m_tree->model()->index(i, 0);
|
||||
m_toplevel->addItem(index.data().toString());
|
||||
auto *item = m_toplevel->item(i);
|
||||
item->setData(Qt::UserRole, index);
|
||||
item->setToolTip(tr("Toplevel Item %1").arg(i));
|
||||
}
|
||||
m_toplevel->setAcceptDrops(true);
|
||||
m_tree->setDragEnabled(true);
|
||||
m_tree->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_toplevel->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
|
||||
connect(m_toplevel, &QListWidget::itemClicked, this, &JsonViewer::onTopLevelItemClicked);
|
||||
connect(m_toplevel, &QListWidget::itemDoubleClicked, this, &JsonViewer::onTopLevelItemDoubleClicked);
|
||||
connect(m_toplevel, &QListWidget::customContextMenuRequested, this, &JsonViewer::onBookmarkMenuRequested);
|
||||
connect(m_tree, &QTreeView::customContextMenuRequested, this, &JsonViewer::onJsonMenuRequested);
|
||||
|
||||
// Connect back and forward
|
||||
connect(m_uiAssets.back, &QAction::triggered, m_tree, [&](){
|
||||
const QModelIndex &index = m_tree->indexAbove(m_tree->currentIndex());
|
||||
if (index.isValid())
|
||||
m_tree->setCurrentIndex(index);
|
||||
});
|
||||
connect(m_uiAssets.forward, &QAction::triggered, m_tree, [&](){
|
||||
QModelIndex current = m_tree->currentIndex();
|
||||
QModelIndex next = m_tree->indexBelow(current);
|
||||
if (next.isValid()) {
|
||||
m_tree->setCurrentIndex(next);
|
||||
return;
|
||||
}
|
||||
|
||||
// Expand last item to go beyond
|
||||
if (!m_tree->isExpanded(current)) {
|
||||
m_tree->expand(current);
|
||||
QModelIndex next = m_tree->indexBelow(current);
|
||||
if (next.isValid()) {
|
||||
m_tree->setCurrentIndex(next);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void resizeToContents(QTreeView *tree)
|
||||
{
|
||||
for (int i = 0; i < tree->header()->count(); ++i)
|
||||
tree->resizeColumnToContents(i);
|
||||
}
|
||||
|
||||
bool JsonViewer::openJsonFile()
|
||||
{
|
||||
disablePrinting();
|
||||
|
||||
QJsonParseError err;
|
||||
m_file->open(QIODevice::ReadOnly);
|
||||
m_root = QJsonDocument::fromJson(m_file->readAll(), &err);
|
||||
const QString type = tr("open");
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
statusMessage(tr("Unable to parse Json document from %1. %2")
|
||||
.arg(QDir::toNativeSeparators(m_file->fileName()),
|
||||
err.errorString()), type);
|
||||
return false;
|
||||
}
|
||||
|
||||
statusMessage(tr("Json document %1 opened")
|
||||
.arg(QDir::toNativeSeparators(m_file->fileName())), type);
|
||||
m_file->close();
|
||||
|
||||
maybeEnablePrinting();
|
||||
|
||||
JsonItemModel *model = new JsonItemModel(m_root, this);
|
||||
m_tree->setModel(model);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QModelIndex indexOf(const QListWidgetItem *item)
|
||||
{
|
||||
return qvariant_cast<QModelIndex>(item->data(Qt::UserRole));
|
||||
}
|
||||
|
||||
// Move to the clicked toplevel index
|
||||
void JsonViewer::onTopLevelItemClicked(QListWidgetItem *item)
|
||||
{
|
||||
// return in the unlikely case that the tree has not been built
|
||||
if (Q_UNLIKELY(!m_tree->model()))
|
||||
return;
|
||||
|
||||
auto index = indexOf(item);
|
||||
if (Q_UNLIKELY(!index.isValid()))
|
||||
return;
|
||||
|
||||
m_tree->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
// Toggle double clicked index between collaps/expand
|
||||
void JsonViewer::onTopLevelItemDoubleClicked(QListWidgetItem *item)
|
||||
{
|
||||
// return in the unlikely case that the tree has not been built
|
||||
if (Q_UNLIKELY(!m_tree->model()))
|
||||
return;
|
||||
|
||||
auto index = indexOf(item);
|
||||
if (Q_UNLIKELY(!index.isValid()))
|
||||
return;
|
||||
|
||||
if (m_tree->isExpanded(index)) {
|
||||
m_tree->collapse(index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the node and all parents are expanded
|
||||
while (index.isValid()) {
|
||||
m_tree->expand(index);
|
||||
index = index.parent();
|
||||
}
|
||||
}
|
||||
|
||||
void JsonViewer::onJsonMenuRequested(const QPoint &pos)
|
||||
{
|
||||
const auto &index = m_tree->indexAt(pos);
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
// Don't show a context menu, if the index is already a bookmark
|
||||
for (int i = 0; i < m_toplevel->count(); ++i) {
|
||||
if (indexOf(m_toplevel->item(i)) == index)
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu menu(m_tree);
|
||||
QAction *action = new QAction(tr("Add bookmark"));
|
||||
action->setData(index);
|
||||
menu.addAction(action);
|
||||
connect(action, &QAction::triggered, this, &JsonViewer::onBookmarkAdded);
|
||||
menu.exec(m_tree->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void JsonViewer::onBookmarkMenuRequested(const QPoint &pos)
|
||||
{
|
||||
auto *item = m_toplevel->itemAt(pos);
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
// Don't delete toplevel items
|
||||
const QModelIndex index = indexOf(item);
|
||||
if (!index.parent().isValid())
|
||||
return;
|
||||
|
||||
QMenu menu;
|
||||
QAction *action = new QAction(tr("Delete bookmark"));
|
||||
action->setData(m_toplevel->row(item));
|
||||
menu.addAction(action);
|
||||
connect(action, &QAction::triggered, this, &JsonViewer::onBookmarkDeleted);
|
||||
menu.exec(m_toplevel->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void JsonViewer::onBookmarkAdded()
|
||||
{
|
||||
const QAction *action = qobject_cast<QAction *>(sender());
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
const QModelIndex index = qvariant_cast<QModelIndex>(action->data());
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
auto *item = new QListWidgetItem(index.data(Qt::DisplayRole).toString(), m_toplevel);
|
||||
item->setData(Qt::UserRole, index);
|
||||
|
||||
// Set a tooltip that shows where the item is located in the tree
|
||||
QModelIndex parent = index.parent();
|
||||
QString tooltip = index.data(Qt::DisplayRole).toString();
|
||||
while (parent.isValid()) {
|
||||
tooltip = parent.data(Qt::DisplayRole).toString() + "->"_L1 + tooltip;
|
||||
parent = parent.parent();
|
||||
}
|
||||
item->setToolTip(tooltip);
|
||||
}
|
||||
|
||||
void JsonViewer::onBookmarkDeleted()
|
||||
{
|
||||
const QAction *action = qobject_cast<QAction *>(sender());
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
const int row = action->data().toInt();
|
||||
if (row < 0 || row >= m_toplevel->count())
|
||||
return;
|
||||
|
||||
delete m_toplevel->takeItem(row);
|
||||
}
|
||||
|
||||
bool JsonViewer::hasContent() const
|
||||
{
|
||||
return !m_root.isEmpty();
|
||||
}
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
void JsonViewer::printDocument(QPrinter *printer) const
|
||||
{
|
||||
if (!hasContent())
|
||||
return;
|
||||
|
||||
const QTextDocument doc(QString::fromUtf8(m_root.toJson(QJsonDocument::JsonFormat::Indented)));
|
||||
doc.print(printer);
|
||||
}
|
||||
|
||||
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
|
||||
QByteArray JsonViewer::saveState() const
|
||||
{
|
||||
QByteArray array;
|
||||
QDataStream stream(&array, QIODevice::WriteOnly);
|
||||
stream << QString(viewerName());
|
||||
stream << m_tree->header()->saveState();
|
||||
return array;
|
||||
}
|
||||
|
||||
bool JsonViewer::restoreState(QByteArray &array)
|
||||
{
|
||||
QDataStream stream(&array, QIODevice::ReadOnly);
|
||||
QString viewer;
|
||||
stream >> viewer;
|
||||
if (viewer != viewerName())
|
||||
return false;
|
||||
QByteArray header;
|
||||
stream >> header;
|
||||
return m_tree->header()->restoreState(header);
|
||||
}
|
||||
|
||||
JsonTreeItem::JsonTreeItem(JsonTreeItem *parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
JsonTreeItem::~JsonTreeItem()
|
||||
{
|
||||
qDeleteAll(m_children);
|
||||
}
|
||||
|
||||
void JsonTreeItem::appendChild(JsonTreeItem *item)
|
||||
{
|
||||
m_children.append(item);
|
||||
}
|
||||
|
||||
JsonTreeItem *JsonTreeItem::child(int row)
|
||||
{
|
||||
return m_children.value(row);
|
||||
}
|
||||
|
||||
JsonTreeItem *JsonTreeItem::parent()
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
int JsonTreeItem::childCount() const
|
||||
{
|
||||
return m_children.count();
|
||||
}
|
||||
|
||||
int JsonTreeItem::row() const
|
||||
{
|
||||
if (m_parent)
|
||||
return m_parent->m_children.indexOf(const_cast<JsonTreeItem*>(this));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void JsonTreeItem::setKey(const QString &key)
|
||||
{
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
void JsonTreeItem::setValue(const QVariant &value)
|
||||
{
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
void JsonTreeItem::setType(const QJsonValue::Type &type)
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
JsonTreeItem* JsonTreeItem::load(const QJsonValue& value, JsonTreeItem* parent)
|
||||
{
|
||||
JsonTreeItem *rootItem = new JsonTreeItem(parent);
|
||||
rootItem->setKey("root"_L1);
|
||||
|
||||
if (value.isObject()) {
|
||||
const QStringList &keys = value.toObject().keys();
|
||||
for (const QString &key : keys) {
|
||||
QJsonValue v = value.toObject().value(key);
|
||||
JsonTreeItem *child = load(v, rootItem);
|
||||
child->setKey(key);
|
||||
child->setType(v.type());
|
||||
rootItem->appendChild(child);
|
||||
}
|
||||
} else if (value.isArray()) {
|
||||
int index = 0;
|
||||
const QJsonArray &array = value.toArray();
|
||||
for (const QJsonValue &val : array) {
|
||||
JsonTreeItem *child = load(val, rootItem);
|
||||
child->setKey(QString::number(index));
|
||||
child->setType(val.type());
|
||||
rootItem->appendChild(child);
|
||||
++index;
|
||||
}
|
||||
} else {
|
||||
rootItem->setValue(value.toVariant());
|
||||
rootItem->setType(value.type());
|
||||
}
|
||||
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
JsonItemModel::JsonItemModel(QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_rootItem{new JsonTreeItem}
|
||||
{
|
||||
m_headers.append("Key"_L1);
|
||||
m_headers.append("Value"_L1);
|
||||
}
|
||||
|
||||
JsonItemModel::JsonItemModel(const QJsonDocument &doc, QObject *parent)
|
||||
: QAbstractItemModel(parent)
|
||||
, m_rootItem{new JsonTreeItem}
|
||||
{
|
||||
// Append header lines and return on empty document
|
||||
m_headers.append("Key"_L1);
|
||||
m_headers.append("Value"_L1);
|
||||
if (doc.isNull())
|
||||
return;
|
||||
|
||||
// Reset the model. Root can either be a value or an array.
|
||||
beginResetModel();
|
||||
delete m_rootItem;
|
||||
if (doc.isArray()) {
|
||||
m_rootItem = JsonTreeItem::load(QJsonValue(doc.array()));
|
||||
m_rootItem->setType(QJsonValue::Array);
|
||||
|
||||
} else {
|
||||
m_rootItem = JsonTreeItem::load(QJsonValue(doc.object()));
|
||||
m_rootItem->setType(QJsonValue::Object);
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
JsonItemModel::~JsonItemModel()
|
||||
{
|
||||
delete m_rootItem;
|
||||
}
|
||||
|
||||
QVariant JsonItemModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
JsonTreeItem *item = itemFromIndex(index);
|
||||
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
if (index.column() == 0)
|
||||
return item->key();
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
break;
|
||||
case Qt::EditRole:
|
||||
if (index.column() == 1)
|
||||
return item->value();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariant JsonItemModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (role != Qt::DisplayRole)
|
||||
return {};
|
||||
|
||||
if (orientation == Qt::Horizontal)
|
||||
return m_headers.value(section);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex JsonItemModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (!hasIndex(row, column, parent))
|
||||
return {};
|
||||
|
||||
JsonTreeItem *parentItem;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = m_rootItem;
|
||||
else
|
||||
parentItem = itemFromIndex(parent);
|
||||
|
||||
JsonTreeItem *childItem = parentItem->child(row);
|
||||
if (childItem)
|
||||
return createIndex(row, column, childItem);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
QModelIndex JsonItemModel::parent(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return {};
|
||||
|
||||
JsonTreeItem *childItem = itemFromIndex(index);
|
||||
JsonTreeItem *parentItem = childItem->parent();
|
||||
|
||||
if (parentItem == m_rootItem)
|
||||
return QModelIndex();
|
||||
|
||||
return createIndex(parentItem->row(), 0, parentItem);
|
||||
}
|
||||
|
||||
int JsonItemModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
JsonTreeItem *parentItem;
|
||||
if (parent.column() > 0)
|
||||
return 0;
|
||||
|
||||
if (!parent.isValid())
|
||||
parentItem = m_rootItem;
|
||||
else
|
||||
parentItem = itemFromIndex(parent);
|
||||
|
||||
return parentItem->childCount();
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef JSONVIEWER_H
|
||||
#define JSONVIEWER_H
|
||||
|
||||
#include "viewerinterfaces.h"
|
||||
|
||||
#include <QJsonValue>
|
||||
#include <QJsonDocument>
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QTreeView;
|
||||
class QListWidget;
|
||||
class QListWidgetItem;
|
||||
class QLineEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class JsonViewer : public ViewerInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0" FILE "jsonviewer.json")
|
||||
Q_INTERFACES(ViewerInterface)
|
||||
public:
|
||||
JsonViewer();
|
||||
~JsonViewer() override;
|
||||
|
||||
void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
|
||||
QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); };
|
||||
QStringList supportedMimeTypes() const override;
|
||||
QByteArray saveState() const override;
|
||||
bool restoreState(QByteArray &) override;
|
||||
bool supportsOverview() const override { return true; }
|
||||
bool hasContent() const override;
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
protected:
|
||||
void printDocument(QPrinter *printer) const override;
|
||||
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT
|
||||
|
||||
private slots:
|
||||
void setupJsonUi();
|
||||
void onTopLevelItemClicked(QListWidgetItem *item);
|
||||
void onTopLevelItemDoubleClicked(QListWidgetItem *item);
|
||||
void onJsonMenuRequested(const QPoint &pos);
|
||||
void onBookmarkMenuRequested(const QPoint &pos);
|
||||
void onBookmarkAdded();
|
||||
void onBookmarkDeleted();
|
||||
|
||||
private:
|
||||
bool openJsonFile();
|
||||
|
||||
QTreeView *m_tree;
|
||||
QListWidget *m_toplevel = nullptr;
|
||||
QJsonDocument m_root;
|
||||
|
||||
QPointer<QLineEdit> m_searchKey;
|
||||
};
|
||||
|
||||
class JsonTreeItem
|
||||
{
|
||||
public:
|
||||
JsonTreeItem(JsonTreeItem *parent = nullptr);
|
||||
~JsonTreeItem();
|
||||
void appendChild(JsonTreeItem *item);
|
||||
JsonTreeItem *child(int row);
|
||||
JsonTreeItem *parent();
|
||||
int childCount() const;
|
||||
int row() const;
|
||||
void setKey(const QString& key);
|
||||
void setValue(const QVariant& value);
|
||||
void setType(const QJsonValue::Type& type);
|
||||
QString key() const { return m_key; };
|
||||
QVariant value() const { return m_value; };
|
||||
QJsonValue::Type type() const { return m_type; };
|
||||
|
||||
static JsonTreeItem* load(const QJsonValue& value, JsonTreeItem *parent = nullptr);
|
||||
|
||||
private:
|
||||
QString m_key;
|
||||
QVariant m_value;
|
||||
QJsonValue::Type m_type;
|
||||
QList<JsonTreeItem*> m_children;
|
||||
JsonTreeItem *m_parent = nullptr;
|
||||
};
|
||||
|
||||
class JsonItemModel : public QAbstractItemModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit JsonItemModel(QObject *parent = nullptr);
|
||||
JsonItemModel(const QJsonDocument& doc, QObject *parent = nullptr);
|
||||
~JsonItemModel();
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
|
||||
QModelIndex parent(const QModelIndex &index) const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
int columnCount(const QModelIndex & = QModelIndex()) const override { return 2; };
|
||||
|
||||
private:
|
||||
JsonTreeItem *m_rootItem = nullptr;
|
||||
QStringList m_headers;
|
||||
static JsonTreeItem *itemFromIndex(const QModelIndex &index)
|
||||
{return static_cast<JsonTreeItem*>(index.internalPointer()); }
|
||||
};
|
||||
|
||||
#endif //JSONVIEWER_H
|
||||
@@ -0,0 +1 @@
|
||||
{ "Keys": [ "jsonviewer" ] }
|
||||
@@ -0,0 +1,39 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets PdfWidgets
|
||||
OPTIONAL_COMPONENTS PrintSupport)
|
||||
|
||||
qt_add_plugin(pdfviewer
|
||||
CLASS_NAME PdfViewer
|
||||
pdfviewer.cpp pdfviewer.h
|
||||
zoomselector.cpp zoomselector.h
|
||||
hoverwatcher.cpp hoverwatcher.h
|
||||
)
|
||||
|
||||
set_target_properties(pdfviewer PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/app"
|
||||
)
|
||||
|
||||
target_include_directories(pdfviewer PRIVATE
|
||||
../../app
|
||||
..
|
||||
)
|
||||
|
||||
target_link_libraries(pdfviewer PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
Qt6::PdfWidgets
|
||||
abstractviewer
|
||||
)
|
||||
|
||||
if(TARGET Qt6::PrintSupport)
|
||||
target_link_libraries(pdfviewer PRIVATE Qt6::PrintSupport)
|
||||
endif()
|
||||
|
||||
install(TARGETS pdfviewer
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
#include "hoverwatcher.h"
|
||||
#include <QGuiApplication>
|
||||
#include <QWidget>
|
||||
#include <QMouseEvent>
|
||||
|
||||
HoverWatcher::HoverWatcher(QWidget *watched)
|
||||
: QObject(watched), m_watched(watched)
|
||||
{
|
||||
Q_ASSERT(watched);
|
||||
m_cursorShapes[Entered].emplace(Qt::OpenHandCursor);
|
||||
m_cursorShapes[MousePress].emplace(Qt::ClosedHandCursor);
|
||||
m_cursorShapes[MouseRelease].emplace(Qt::OpenHandCursor);
|
||||
// no default for Left => restore override cursor
|
||||
m_watched->installEventFilter(this);
|
||||
}
|
||||
|
||||
HoverWatcher::~HoverWatcher()
|
||||
{
|
||||
m_watched->removeEventFilter(this);
|
||||
}
|
||||
|
||||
typedef QHash<QWidget *, HoverWatcher*> WatchMap;
|
||||
Q_GLOBAL_STATIC(WatchMap, qt_allHoverWatchers)
|
||||
|
||||
HoverWatcher *HoverWatcher::watcher(QWidget *watched)
|
||||
{
|
||||
if (qt_allHoverWatchers()->contains(watched))
|
||||
return qt_allHoverWatchers()->value(watched);
|
||||
|
||||
HoverWatcher *watcher = new HoverWatcher(watched);
|
||||
qt_allHoverWatchers()->insert(watched, watcher);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload Const version of watcher
|
||||
*/
|
||||
const HoverWatcher *HoverWatcher::watcher(const QWidget *watched)
|
||||
{
|
||||
return watcher(const_cast<QWidget *>(watched));
|
||||
}
|
||||
|
||||
void HoverWatcher::dismiss(QWidget *watched)
|
||||
{
|
||||
if (!hasWatcher(watched))
|
||||
return;
|
||||
|
||||
delete qt_allHoverWatchers()->take(watched);
|
||||
}
|
||||
|
||||
bool HoverWatcher::hasWatcher(QWidget *widget)
|
||||
{
|
||||
return qt_allHoverWatchers()->contains(widget);
|
||||
}
|
||||
|
||||
static constexpr HoverWatcher::HoverAction toHoverAction(QEvent::Type et)
|
||||
{
|
||||
switch (et) {
|
||||
case QEvent::Type::Enter:
|
||||
return HoverWatcher::HoverAction::Entered;
|
||||
case QEvent::Type::Leave:
|
||||
return HoverWatcher::HoverAction::Left;
|
||||
case QEvent::Type::MouseButtonPress:
|
||||
return HoverWatcher::HoverAction::MousePress;
|
||||
case QEvent::Type::MouseButtonRelease:
|
||||
return HoverWatcher::HoverAction::MouseRelease;
|
||||
default:
|
||||
return HoverWatcher::HoverAction::Ignore;
|
||||
}
|
||||
}
|
||||
|
||||
void HoverWatcher::handleAction (HoverWatcher::HoverAction action)
|
||||
{
|
||||
const Qt::CursorShape newShape = cursorShape(action);
|
||||
if (QGuiApplication::overrideCursor()
|
||||
&& (QGuiApplication::overrideCursor()->shape() == newShape
|
||||
|| action == HoverAction::Ignore)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QGuiApplication::setOverrideCursor(cursorShape(action));
|
||||
emit hoverAction(action);
|
||||
|
||||
switch (action) {
|
||||
case HoverAction::Entered:
|
||||
emit entered();
|
||||
break;
|
||||
case HoverAction::Left:
|
||||
emit left();
|
||||
break;
|
||||
case HoverAction::MousePress:
|
||||
emit mousePressed();
|
||||
break;
|
||||
case HoverAction::MouseRelease: {
|
||||
emit mouseReleased();
|
||||
}
|
||||
break;
|
||||
case HoverAction::Ignore:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool HoverWatcher::hasShape(HoverAction action) const
|
||||
{
|
||||
return action != HoverAction::Ignore && m_cursorShapes[action].has_value();
|
||||
}
|
||||
|
||||
void HoverWatcher::setApplicationCursor(HoverAction action) const
|
||||
{
|
||||
if (!hasShape(action)) {
|
||||
QGuiApplication::restoreOverrideCursor();
|
||||
return;
|
||||
}
|
||||
|
||||
QGuiApplication::setOverrideCursor(cursorShape(action));
|
||||
}
|
||||
|
||||
bool HoverWatcher::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
Q_ASSERT(obj == m_watched); // don't install event filters elsewhere
|
||||
|
||||
// Ignore irrelevant events
|
||||
const auto action = toHoverAction(event->type());
|
||||
if (action == HoverAction::Ignore)
|
||||
return false;
|
||||
|
||||
// React to a QScroller having been installed or removed
|
||||
// A Scroller sends a fake mouse release to QPoint (-1, -1)
|
||||
// => needs to be ignored and end of scrolling processed instead
|
||||
static bool hasScroller = false;
|
||||
if (QScroller::hasScroller(m_watched) != hasScroller) {
|
||||
hasScroller = QScroller::hasScroller(m_watched);
|
||||
static QMetaObject::Connection con;
|
||||
if (hasScroller) {
|
||||
con = connect(QScroller::scroller(m_watched), &QScroller::stateChanged,
|
||||
this, &HoverWatcher::handleScrollerStateChange);
|
||||
} else {
|
||||
disconnect(con);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore fake mouse release event sent by scroller
|
||||
if (action == HoverAction::MouseRelease && hasScroller) {
|
||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||
if (me->pos().x() < -9000000 )
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ignore unpermitted mouse buttons
|
||||
if (action == HoverAction::MousePress) {
|
||||
QMouseEvent *me = static_cast<QMouseEvent *>(event);
|
||||
if (!m_mouseButtons.testFlag(me->button()))
|
||||
return false;
|
||||
}
|
||||
|
||||
handleAction(action);
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::CursorShape HoverWatcher::cursorShape(HoverAction type) const
|
||||
{
|
||||
const Qt::CursorShape fallback = Qt::ArrowCursor;
|
||||
if (type == HoverAction::Ignore)
|
||||
return fallback;
|
||||
|
||||
return m_cursorShapes[type].value_or(fallback);
|
||||
}
|
||||
|
||||
void HoverWatcher::setCursorShape(HoverAction type, Qt::CursorShape shape)
|
||||
{
|
||||
if (type == HoverAction::Ignore)
|
||||
return;
|
||||
m_cursorShapes[type].emplace(shape);
|
||||
}
|
||||
|
||||
void HoverWatcher::unSetCursorShape(HoverAction type)
|
||||
{
|
||||
if (type == HoverAction::Ignore)
|
||||
return;
|
||||
m_cursorShapes[type].reset();
|
||||
}
|
||||
|
||||
void HoverWatcher::setMouseButtons(Qt::MouseButtons buttons)
|
||||
{
|
||||
m_mouseButtons = buttons;
|
||||
}
|
||||
|
||||
void HoverWatcher::setMouseButton(Qt::MouseButton button, bool enable)
|
||||
{
|
||||
m_mouseButtons.setFlag(button, enable);;
|
||||
}
|
||||
|
||||
/*!
|
||||
\brief This slot handles a QScroller state change, in case the watched
|
||||
widget uses a scroller. It translates \param state into the appropriate
|
||||
action.
|
||||
*/
|
||||
void HoverWatcher::handleScrollerStateChange(QScroller::State state)
|
||||
{
|
||||
switch (state) {
|
||||
case QScroller::State::Pressed:
|
||||
case QScroller::State::Dragging:
|
||||
case QScroller::State::Scrolling:
|
||||
handleAction(HoverAction::MousePress);
|
||||
break;
|
||||
case QScroller::State::Inactive:
|
||||
handleAction(HoverAction::MouseRelease);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef HOVERWATCHER_H
|
||||
#define HOVERWATCHER_H
|
||||
#include <QObject>
|
||||
#include <QEvent>
|
||||
#include <QScroller>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QWidget;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class HoverWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
explicit HoverWatcher(QWidget *watched);
|
||||
static QMap<QWidget *, HoverWatcher *> m_hoverWatchers;
|
||||
|
||||
public:
|
||||
~HoverWatcher();
|
||||
|
||||
enum HoverAction {
|
||||
Entered,
|
||||
MousePress,
|
||||
MouseRelease,
|
||||
Left,
|
||||
Ignore
|
||||
};
|
||||
Q_ENUM(HoverAction);
|
||||
|
||||
bool eventFilter(QObject *obj, QEvent *event) override;
|
||||
|
||||
Qt::CursorShape cursorShape(HoverAction type) const;
|
||||
Qt::MouseButtons mouseButtons() const { return m_mouseButtons; }
|
||||
|
||||
static HoverWatcher *watcher(QWidget *watched);
|
||||
static const HoverWatcher *watcher(const QWidget *watched);
|
||||
static bool hasWatcher(QWidget *widget);
|
||||
static void dismiss(QWidget *watched);
|
||||
|
||||
public slots:
|
||||
void setCursorShape(HoverAction type, Qt::CursorShape shape);
|
||||
void unSetCursorShape(HoverAction type);
|
||||
void setMouseButtons(Qt::MouseButtons buttons);
|
||||
void setMouseButton(Qt::MouseButton button, bool enable);
|
||||
|
||||
signals:
|
||||
void entered();
|
||||
void mousePressed();
|
||||
void mouseReleased();
|
||||
void left();
|
||||
void hoverAction(HoverAction action);
|
||||
|
||||
private slots:
|
||||
void handleScrollerStateChange(QScroller::State state);
|
||||
|
||||
private:
|
||||
QWidget *m_watched;
|
||||
std::array<std::optional<Qt::CursorShape>, HoverAction::Ignore> m_cursorShapes;
|
||||
Qt::MouseButtons m_mouseButtons = Qt::MouseButton::LeftButton;
|
||||
void handleAction(HoverAction action);
|
||||
void setApplicationCursor(HoverAction action) const;
|
||||
bool hasShape(HoverAction action) const;
|
||||
};
|
||||
|
||||
#endif // HOVERWATCHER_H
|
||||
@@ -0,0 +1,266 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "pdfviewer.h"
|
||||
#include "zoomselector.h"
|
||||
#include "hoverwatcher.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QEvent>
|
||||
#include <QFile>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include <QPdfBookmarkModel>
|
||||
#include <QPdfDocument>
|
||||
#include <QPdfPageNavigator>
|
||||
#include <QPdfView>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6,6,0)
|
||||
#include <QPdfPageSelector>
|
||||
#endif
|
||||
|
||||
#include <QtMath>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QListView>
|
||||
#include <QPdfView>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QtMath>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QListView>
|
||||
#include <QListWidget>
|
||||
#include <QMainWindow>
|
||||
#include <QScrollBar>
|
||||
#include <QScroller>
|
||||
#include <QSpinBox>
|
||||
#include <QToolBar>
|
||||
#include <QTreeView>
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
#include <QPrinter>
|
||||
#include <QPainter>
|
||||
#endif
|
||||
|
||||
Q_LOGGING_CATEGORY(lcExample, "qt.examples.pdfviewer")
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
PdfViewer::PdfViewer()
|
||||
{
|
||||
connect(this, &AbstractViewer::uiInitialized, this, &PdfViewer::initPdfViewer);
|
||||
}
|
||||
|
||||
void PdfViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
|
||||
{
|
||||
AbstractViewer::init(file, new QPdfView(parent), mainWindow);
|
||||
m_document = new QPdfDocument(this);
|
||||
m_pdfView = qobject_cast<QPdfView *>(widget());
|
||||
}
|
||||
|
||||
void PdfViewer::cleanup()
|
||||
{
|
||||
delete m_pageSelector;
|
||||
m_pageSelector = nullptr;
|
||||
delete m_zoomSelector;
|
||||
m_zoomSelector = nullptr;
|
||||
delete m_pages;
|
||||
m_pages = nullptr;
|
||||
delete m_bookmarks;
|
||||
m_bookmarks = nullptr;
|
||||
delete m_document;
|
||||
m_document = nullptr;
|
||||
AbstractViewer::cleanup();
|
||||
}
|
||||
|
||||
PdfViewer::~PdfViewer()
|
||||
{
|
||||
PdfViewer::cleanup();
|
||||
}
|
||||
|
||||
QStringList PdfViewer::supportedMimeTypes() const
|
||||
{
|
||||
return {"application/pdf"_L1};
|
||||
}
|
||||
|
||||
void PdfViewer::initPdfViewer()
|
||||
{
|
||||
m_toolBar = addToolBar(tr("PDF"));
|
||||
m_zoomSelector = new ZoomSelector(m_toolBar);
|
||||
|
||||
auto *nav = m_pdfView->pageNavigator();
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6,6,0)
|
||||
m_pageSelector = new QPdfPageSelector(m_toolBar);
|
||||
m_toolBar->insertWidget(m_uiAssets.forward, m_pageSelector);
|
||||
m_pageSelector->setDocument(m_document);
|
||||
connect(m_pageSelector, &QPdfPageSelector::currentPageChanged,
|
||||
this, &PdfViewer::pageSelected);
|
||||
connect(m_pageSelector, &QPdfPageSelector::currentPageChanged,
|
||||
this, &PdfViewer::pageSelected);
|
||||
connect(nav, &QPdfPageNavigator::currentPageChanged,
|
||||
m_pageSelector, &QPdfPageSelector::setCurrentPage);
|
||||
#endif
|
||||
|
||||
connect(m_pdfView->pageNavigator(), &QPdfPageNavigator::backAvailableChanged,
|
||||
m_uiAssets.back, &QAction::setEnabled);
|
||||
m_actionBack = m_uiAssets.back;
|
||||
m_actionForward = m_uiAssets.forward;
|
||||
m_connections.append(connect(m_uiAssets.back, &QAction::triggered,
|
||||
this, &PdfViewer::onActionBackTriggered));
|
||||
m_connections.append(connect(m_uiAssets.forward, &QAction::triggered,
|
||||
this, &PdfViewer::onActionForwardTriggered));
|
||||
|
||||
m_toolBar->addSeparator();
|
||||
m_toolBar->addWidget(m_zoomSelector);
|
||||
|
||||
auto *actionZoomIn = m_toolBar->addAction(tr("Zoom in"));
|
||||
actionZoomIn->setToolTip(tr("Increase zoom level"));
|
||||
actionZoomIn->setIcon(QIcon(":/demos/documentviewer/images/zoom-in.png"_L1));
|
||||
m_toolBar->addAction(actionZoomIn);
|
||||
connect(actionZoomIn, &QAction::triggered, this, &PdfViewer::onActionZoomInTriggered);
|
||||
|
||||
auto *actionZoomOut = m_toolBar->addAction(tr("Zoom out"));
|
||||
actionZoomOut->setToolTip(tr("Decrease zoom level"));
|
||||
actionZoomOut->setIcon(QIcon(":/demos/documentviewer/images/zoom-out.png"_L1));
|
||||
m_toolBar->addAction(actionZoomOut);
|
||||
connect(actionZoomOut, &QAction::triggered, this, &PdfViewer::onActionZoomOutTriggered);
|
||||
|
||||
connect(nav, &QPdfPageNavigator::backAvailableChanged, m_actionBack, &QAction::setEnabled);
|
||||
connect(nav, &QPdfPageNavigator::forwardAvailableChanged, m_actionForward, &QAction::setEnabled);
|
||||
|
||||
connect(m_zoomSelector, &ZoomSelector::zoomModeChanged, m_pdfView, &QPdfView::setZoomMode);
|
||||
connect(m_zoomSelector, &ZoomSelector::zoomFactorChanged, m_pdfView, &QPdfView::setZoomFactor);
|
||||
m_zoomSelector->reset();
|
||||
|
||||
QPdfBookmarkModel *bookmarkModel = new QPdfBookmarkModel(this);
|
||||
bookmarkModel->setDocument(m_document);
|
||||
m_uiAssets.tabs->clear();
|
||||
m_bookmarks = new QTreeView(m_uiAssets.tabs);
|
||||
connect(m_bookmarks, &QAbstractItemView::activated, this, &PdfViewer::bookmarkSelected);
|
||||
m_bookmarks->setModel(bookmarkModel);
|
||||
m_pdfView->setDocument(m_document);
|
||||
m_pdfView->setPageMode(QPdfView::PageMode::MultiPage);
|
||||
|
||||
openPdfFile();
|
||||
if (!m_document->pageCount())
|
||||
return;
|
||||
|
||||
m_pages = new QListView(m_uiAssets.tabs);
|
||||
m_pages->setModel(m_document->pageModel());
|
||||
connect(m_pages->selectionModel(), &QItemSelectionModel::currentRowChanged, m_pages, [&]
|
||||
(const QModelIndex ¤t, const QModelIndex &previous){
|
||||
if (previous == current)
|
||||
return;
|
||||
|
||||
auto *nav = m_pdfView->pageNavigator();
|
||||
const int &row = current.row();
|
||||
if (nav->currentPage() == row)
|
||||
return;
|
||||
|
||||
nav->jump(row, QPointF(), nav->currentZoom());
|
||||
});
|
||||
|
||||
connect(m_pdfView->pageNavigator(), &QPdfPageNavigator::currentPageChanged, m_pages, [&](int page){
|
||||
if (m_pages->currentIndex().row() == page)
|
||||
return;
|
||||
|
||||
m_pages->setCurrentIndex(m_pages->model()->index(page, 0));
|
||||
});
|
||||
|
||||
m_uiAssets.tabs->addTab(m_pages, tr("Pages"));
|
||||
m_uiAssets.tabs->addTab(m_bookmarks, tr("Bookmarks"));
|
||||
QScroller::grabGesture(m_pdfView->viewport(), QScroller::ScrollerGestureType::LeftMouseButtonGesture);
|
||||
HoverWatcher::watcher(m_pdfView->viewport());
|
||||
}
|
||||
|
||||
void PdfViewer::openPdfFile()
|
||||
{
|
||||
disablePrinting();
|
||||
|
||||
if (m_file->open(QIODevice::ReadOnly))
|
||||
m_document->load(m_file.get());
|
||||
|
||||
const auto documentTitle = m_document->metaData(QPdfDocument::MetaDataField::Title).toString();
|
||||
statusMessage(documentTitle.isEmpty() ? "PDF Viewer"_L1 : documentTitle);
|
||||
pageSelected(0);
|
||||
|
||||
statusMessage(tr("Opened PDF file %1")
|
||||
.arg(QDir::toNativeSeparators(m_file->fileName())));
|
||||
qCDebug(lcExample) << "Opened file" << m_file->fileName();
|
||||
|
||||
maybeEnablePrinting();
|
||||
}
|
||||
|
||||
bool PdfViewer::hasContent() const
|
||||
{
|
||||
return m_document ? m_document->pageCount() > 0 : false;
|
||||
}
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
void PdfViewer::printDocument(QPrinter *printer) const
|
||||
{
|
||||
if (!hasContent())
|
||||
return;
|
||||
|
||||
QPainter painter;
|
||||
painter.begin(printer);
|
||||
const QRect pageRect = printer->pageRect(QPrinter::Unit::DevicePixel).toRect();
|
||||
const QSize pageSize = pageRect.size();
|
||||
for (int i = 0; i < m_document->pageCount(); ++i) {
|
||||
if (i > 0)
|
||||
printer->newPage();
|
||||
const QImage &page = m_document->render(i, pageSize);
|
||||
painter.drawImage(pageRect, page);
|
||||
}
|
||||
painter.end();
|
||||
}
|
||||
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
|
||||
void PdfViewer::bookmarkSelected(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
const int page = index.data(int(QPdfBookmarkModel::Role::Page)).toInt();
|
||||
const qreal zoomLevel = index.data(int(QPdfBookmarkModel::Role::Level)).toReal();
|
||||
m_pdfView->pageNavigator()->jump(page, {}, zoomLevel);
|
||||
}
|
||||
|
||||
void PdfViewer::pageSelected(int page)
|
||||
{
|
||||
auto nav = m_pdfView->pageNavigator();
|
||||
nav->jump(page, {}, nav->currentZoom());
|
||||
}
|
||||
|
||||
void PdfViewer::onActionZoomInTriggered()
|
||||
{
|
||||
m_pdfView->setZoomFactor(m_pdfView->zoomFactor() * zoomMultiplier);
|
||||
}
|
||||
|
||||
void PdfViewer::onActionZoomOutTriggered()
|
||||
{
|
||||
m_pdfView->setZoomFactor(m_pdfView->zoomFactor() / zoomMultiplier);
|
||||
}
|
||||
|
||||
void PdfViewer::onActionPreviousPageTriggered()
|
||||
{
|
||||
auto nav = m_pdfView->pageNavigator();
|
||||
nav->jump(nav->currentPage() - 1, {}, nav->currentZoom());
|
||||
}
|
||||
|
||||
void PdfViewer::onActionNextPageTriggered()
|
||||
{
|
||||
auto nav = m_pdfView->pageNavigator();
|
||||
nav->jump(nav->currentPage() + 1, {}, nav->currentZoom());
|
||||
}
|
||||
|
||||
void PdfViewer::onActionBackTriggered()
|
||||
{
|
||||
m_pdfView->pageNavigator()->back();
|
||||
}
|
||||
|
||||
void PdfViewer::onActionForwardTriggered()
|
||||
{
|
||||
m_pdfView->pageNavigator()->forward();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef PDFVIEWER_H
|
||||
#define PDFVIEWER_H
|
||||
|
||||
#include "viewerinterfaces.h"
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcExample)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QMainWindow;
|
||||
class QPdfDocument;
|
||||
class QPdfView;
|
||||
class QPdfPageSelector;
|
||||
class QListView;
|
||||
class QTabWidget;
|
||||
class QTreeView;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class ZoomSelector;
|
||||
class PdfViewer : public ViewerInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "pdfviewer.json")
|
||||
Q_INTERFACES(ViewerInterface)
|
||||
public:
|
||||
PdfViewer();
|
||||
~PdfViewer() override;
|
||||
void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
|
||||
void cleanup() override;
|
||||
QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); };
|
||||
QStringList supportedMimeTypes() const override;
|
||||
bool supportsOverview() const override { return true; }
|
||||
bool hasContent() const override;
|
||||
QByteArray saveState() const override { return QByteArray(); }
|
||||
bool restoreState(QByteArray &) override { return true; }
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
protected:
|
||||
void printDocument(QPrinter *printer) const override;
|
||||
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
|
||||
public slots:
|
||||
void openPdfFile();
|
||||
|
||||
private slots:
|
||||
void initPdfViewer();
|
||||
void bookmarkSelected(const QModelIndex &index);
|
||||
void pageSelected(int page);
|
||||
|
||||
// action handlers
|
||||
void onActionZoomInTriggered();
|
||||
void onActionZoomOutTriggered();
|
||||
void onActionPreviousPageTriggered();
|
||||
void onActionNextPageTriggered();
|
||||
void onActionBackTriggered();
|
||||
void onActionForwardTriggered();
|
||||
|
||||
private:
|
||||
void populateQuestions();
|
||||
|
||||
const qreal zoomMultiplier = qSqrt(2.0);
|
||||
QToolBar *m_toolBar = nullptr;
|
||||
ZoomSelector *m_zoomSelector = nullptr;
|
||||
QPdfPageSelector *m_pageSelector = nullptr;
|
||||
QPdfDocument *m_document = nullptr;
|
||||
QPdfView *m_pdfView = nullptr;
|
||||
QAction *m_actionForward = nullptr;
|
||||
QAction *m_actionBack = nullptr;
|
||||
QTreeView *m_bookmarks = nullptr;
|
||||
QListView *m_pages = nullptr;
|
||||
};
|
||||
|
||||
#endif //PDFVIEWER_H
|
||||
@@ -0,0 +1 @@
|
||||
{ "Keys": [ "pdfviewer" ] }
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include "zoomselector.h"
|
||||
|
||||
#include <QLineEdit>
|
||||
|
||||
ZoomSelector::ZoomSelector(QWidget *parent)
|
||||
: QComboBox(parent)
|
||||
{
|
||||
setEditable(true);
|
||||
|
||||
addItem(tr("Fit Width"));
|
||||
addItem(tr("Fit Page"));
|
||||
addItem(tr("12%"));
|
||||
addItem(tr("25%"));
|
||||
addItem(tr("33%"));
|
||||
addItem(tr("50%"));
|
||||
addItem(tr("66%"));
|
||||
addItem(tr("75%"));
|
||||
addItem(tr("100%"));
|
||||
addItem(tr("125%"));
|
||||
addItem(tr("150%"));
|
||||
addItem(tr("200%"));
|
||||
addItem(tr("400%"));
|
||||
|
||||
connect(this, &QComboBox::currentTextChanged,
|
||||
this, &ZoomSelector::onCurrentTextChanged);
|
||||
|
||||
connect(lineEdit(), &QLineEdit::editingFinished,
|
||||
this, [this](){onCurrentTextChanged(lineEdit()->text()); });
|
||||
}
|
||||
|
||||
void ZoomSelector::setZoomFactor(qreal zoomFactor)
|
||||
{
|
||||
setCurrentText(QString::number(qRound(zoomFactor * 100)) + QLatin1String("%"));
|
||||
}
|
||||
|
||||
void ZoomSelector::reset()
|
||||
{
|
||||
setCurrentIndex(8); // 100%
|
||||
}
|
||||
|
||||
void ZoomSelector::onCurrentTextChanged(const QString &text)
|
||||
{
|
||||
if (text == QLatin1String("Fit Width")) {
|
||||
emit zoomModeChanged(QPdfView::ZoomMode::FitToWidth);
|
||||
} else if (text == QLatin1String("Fit Page")) {
|
||||
emit zoomModeChanged(QPdfView::ZoomMode::FitInView);
|
||||
} else {
|
||||
qreal factor = 1.0;
|
||||
|
||||
QString withoutPercent(text);
|
||||
withoutPercent.remove(QLatin1Char('%'));
|
||||
|
||||
bool ok = false;
|
||||
const int zoomLevel = withoutPercent.toInt(&ok);
|
||||
if (ok)
|
||||
factor = zoomLevel / 100.0;
|
||||
|
||||
emit zoomModeChanged(QPdfView::ZoomMode::Custom);
|
||||
emit zoomFactorChanged(factor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef ZOOMSELECTOR_H
|
||||
#define ZOOMSELECTOR_H
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QPdfView>
|
||||
|
||||
class ZoomSelector : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ZoomSelector(QWidget *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
void setZoomFactor(qreal zoomFactor);
|
||||
|
||||
void reset();
|
||||
|
||||
signals:
|
||||
void zoomModeChanged(QPdfView::ZoomMode zoomMode);
|
||||
void zoomFactorChanged(qreal zoomFactor);
|
||||
|
||||
private slots:
|
||||
void onCurrentTextChanged(const QString &text);
|
||||
};
|
||||
|
||||
#endif // ZOOMSELECTOR_H
|
||||
@@ -0,0 +1,35 @@
|
||||
# Copyright (C) 2023 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets
|
||||
OPTIONAL_COMPONENTS PrintSupport)
|
||||
|
||||
qt_add_plugin(txtviewer
|
||||
CLASS_NAME TxtViewer
|
||||
txtviewer.cpp txtviewer.h
|
||||
)
|
||||
|
||||
set_target_properties(txtviewer PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/app"
|
||||
)
|
||||
|
||||
target_include_directories(txtviewer PRIVATE
|
||||
../../app
|
||||
)
|
||||
|
||||
target_link_libraries(txtviewer PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Widgets
|
||||
abstractviewer
|
||||
)
|
||||
|
||||
if(TARGET Qt6::PrintSupport)
|
||||
target_link_libraries(txtviewer PRIVATE Qt6::PrintSupport)
|
||||
endif()
|
||||
|
||||
install(TARGETS jsonviewer
|
||||
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
|
||||
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}/plugins"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
//! [init]
|
||||
#include "txtviewer.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QScrollBar>
|
||||
#include <QToolBar>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QPainter>
|
||||
#include <QTextDocument>
|
||||
|
||||
#include <QDir>
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
#include <QPrinter>
|
||||
#include <QPrintDialog>
|
||||
#endif
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
TxtViewer::TxtViewer()
|
||||
{
|
||||
connect(this, &AbstractViewer::uiInitialized, this, &TxtViewer::setupTxtUi);
|
||||
}
|
||||
|
||||
TxtViewer::~TxtViewer() = default;
|
||||
|
||||
void TxtViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
|
||||
{
|
||||
AbstractViewer::init(file, new QPlainTextEdit(parent), mainWindow);
|
||||
m_textEdit = qobject_cast<QPlainTextEdit *>(widget());
|
||||
}
|
||||
|
||||
QStringList TxtViewer::supportedMimeTypes() const
|
||||
{
|
||||
return {"text/plain"_L1};
|
||||
}
|
||||
|
||||
void TxtViewer::setupTxtUi()
|
||||
{
|
||||
QMenu *editMenu = addMenu(tr("&Edit"));
|
||||
QToolBar *editToolBar = addToolBar(tr("Edit"));
|
||||
#ifndef QT_NO_CLIPBOARD
|
||||
const QIcon cutIcon = QIcon::fromTheme("edit-cut"_L1,
|
||||
QIcon(":/demos/documentviewer/images/cut.png"_L1));
|
||||
QAction *cutAct = new QAction(cutIcon, tr("Cu&t"), this);
|
||||
cutAct->setShortcuts(QKeySequence::Cut);
|
||||
cutAct->setStatusTip(tr("Cut the current selection's contents to the "
|
||||
"clipboard"));
|
||||
connect(cutAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::cut);
|
||||
editMenu->addAction(cutAct);
|
||||
editToolBar->addAction(cutAct);
|
||||
|
||||
const QIcon copyIcon = QIcon::fromTheme("edit-copy"_L1,
|
||||
QIcon(":/demos/documentviewer/images/copy.png"_L1));
|
||||
QAction *copyAct = new QAction(copyIcon, tr("&Copy"), this);
|
||||
copyAct->setShortcuts(QKeySequence::Copy);
|
||||
copyAct->setStatusTip(tr("Copy the current selection's contents to the "
|
||||
"clipboard"));
|
||||
connect(copyAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::copy);
|
||||
editMenu->addAction(copyAct);
|
||||
editToolBar->addAction(copyAct);
|
||||
|
||||
const QIcon pasteIcon = QIcon::fromTheme("edit-paste"_L1,
|
||||
QIcon(":/demos/documentviewer/images/paste.png"_L1));
|
||||
QAction *pasteAct = new QAction(pasteIcon, tr("&Paste"), this);
|
||||
pasteAct->setShortcuts(QKeySequence::Paste);
|
||||
pasteAct->setStatusTip(tr("Paste the clipboard's contents into the current "
|
||||
"selection"));
|
||||
connect(pasteAct, &QAction::triggered, m_textEdit, &QPlainTextEdit::paste);
|
||||
editMenu->addAction(pasteAct);
|
||||
editToolBar->addAction(pasteAct);
|
||||
|
||||
menuBar()->addSeparator();
|
||||
|
||||
cutAct->setEnabled(false);
|
||||
copyAct->setEnabled(false);
|
||||
connect(m_textEdit, &QPlainTextEdit::copyAvailable, cutAct, &QAction::setEnabled);
|
||||
connect(m_textEdit, &QPlainTextEdit::copyAvailable, copyAct, &QAction::setEnabled);
|
||||
#endif // !QT_NO_CLIPBOARD
|
||||
|
||||
openFile();
|
||||
|
||||
connect(m_textEdit, &QPlainTextEdit::textChanged, this, [&](){
|
||||
maybeSetPrintingEnabled(hasContent());
|
||||
});
|
||||
|
||||
connect(m_uiAssets.back, &QAction::triggered, m_textEdit, [&](){
|
||||
auto *bar = m_textEdit->verticalScrollBar();
|
||||
if (bar->value() > bar->minimum())
|
||||
bar->setValue(bar->value() - 1);
|
||||
});
|
||||
|
||||
connect(m_uiAssets.forward, &QAction::triggered, m_textEdit, [&](){
|
||||
auto *bar = m_textEdit->verticalScrollBar();
|
||||
if (bar->value() < bar->maximum())
|
||||
bar->setValue(bar->value() + 1);
|
||||
});
|
||||
}
|
||||
//! [init]
|
||||
|
||||
//! [open]
|
||||
void TxtViewer::openFile()
|
||||
{
|
||||
const QString type = tr("open");
|
||||
if (!m_file->open(QFile::ReadOnly | QFile::Text)) {
|
||||
statusMessage(tr("Cannot read file %1:\n%2.")
|
||||
.arg(QDir::toNativeSeparators(m_file->fileName()),
|
||||
m_file->errorString()), type);
|
||||
return;
|
||||
}
|
||||
|
||||
QTextStream in(m_file.get());
|
||||
#ifndef QT_NO_CURSOR
|
||||
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
#endif
|
||||
if (!m_textEdit->toPlainText().isEmpty()) {
|
||||
m_textEdit->clear();
|
||||
disablePrinting();
|
||||
}
|
||||
m_textEdit->setPlainText(in.readAll());
|
||||
#ifndef QT_NO_CURSOR
|
||||
QGuiApplication::restoreOverrideCursor();
|
||||
#endif
|
||||
|
||||
statusMessage(tr("File %1 loaded.")
|
||||
.arg(QDir::toNativeSeparators(m_file->fileName())), type);
|
||||
maybeEnablePrinting();
|
||||
}
|
||||
//! [open]
|
||||
|
||||
//! [infoPrintAndSave]
|
||||
bool TxtViewer::hasContent() const
|
||||
{
|
||||
return (!m_textEdit->toPlainText().isEmpty());
|
||||
}
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
void TxtViewer::printDocument(QPrinter *printer) const
|
||||
{
|
||||
if (!hasContent())
|
||||
return;
|
||||
|
||||
m_textEdit->print(printer);
|
||||
}
|
||||
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
|
||||
bool TxtViewer::saveFile(QFile *file)
|
||||
{
|
||||
QString errorMessage;
|
||||
|
||||
QGuiApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
if (file->open(QFile::WriteOnly | QFile::Text)) {
|
||||
QTextStream out(file);
|
||||
out << m_textEdit->toPlainText();
|
||||
} else {
|
||||
errorMessage = tr("Cannot open file %1 for writing:\n%2.")
|
||||
.arg(QDir::toNativeSeparators(file->fileName())),
|
||||
file->errorString();
|
||||
}
|
||||
QGuiApplication::restoreOverrideCursor();
|
||||
|
||||
if (!errorMessage.isEmpty()) {
|
||||
statusMessage(errorMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
statusMessage(tr("File %1 saved")
|
||||
.arg(QDir::toNativeSeparators(file->fileName())));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TxtViewer::saveDocumentAs()
|
||||
{
|
||||
QFileDialog dialog(mainWindow());
|
||||
dialog.setWindowModality(Qt::WindowModal);
|
||||
dialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
return false;
|
||||
|
||||
const QStringList &files = dialog.selectedFiles();
|
||||
if (files.isEmpty())
|
||||
return false;
|
||||
|
||||
//newFile();
|
||||
m_file->setFileName(files.first());
|
||||
return saveDocument();
|
||||
}
|
||||
//! [infoPrintAndSave]
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#ifndef TXTVIEWER_H
|
||||
#define TXTVIEWER_H
|
||||
|
||||
#include "viewerinterfaces.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPlainTextEdit;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
//! [interfacing]
|
||||
class TxtViewer : public ViewerInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "txtviewer.json")
|
||||
Q_INTERFACES(ViewerInterface)
|
||||
//! [interfacing]
|
||||
//! [classDefinition]
|
||||
public:
|
||||
TxtViewer();
|
||||
~TxtViewer() override;
|
||||
void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
|
||||
QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); };
|
||||
QStringList supportedMimeTypes() const override;
|
||||
bool saveDocument() override { return saveFile(m_file.get()); };
|
||||
bool saveDocumentAs() override;
|
||||
bool hasContent() const override;
|
||||
QByteArray saveState() const override { return {}; }
|
||||
bool restoreState(QByteArray &) override { return true; }
|
||||
bool supportsOverview() const override { return false; }
|
||||
|
||||
#ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
protected:
|
||||
void printDocument(QPrinter *printer) const override;
|
||||
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
|
||||
|
||||
private slots:
|
||||
void setupTxtUi();
|
||||
|
||||
private:
|
||||
void openFile();
|
||||
bool saveFile (QFile *file);
|
||||
|
||||
QPlainTextEdit *m_textEdit;
|
||||
};
|
||||
//! [classDefinition]
|
||||
|
||||
#endif //TXTVIEWER_H
|
||||
@@ -0,0 +1 @@
|
||||
{ "Keys": [ "txtviewer" ] }
|
||||