making clangd happy and making script to compile/build with run.sh

This commit is contained in:
talksik
2024-02-07 13:12:51 -08:00
commit 45c681e7be
79 changed files with 4235 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
build/
+25
View File
@@ -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)
+417
View File
@@ -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>
+68
View File
@@ -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}"
)
+281
View File
@@ -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();
}
+101
View File
@@ -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
+42
View File
@@ -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>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1017 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

+36
View File
@@ -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();
}
+190
View File
@@ -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);
}
+56
View File
@@ -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
+272
View File
@@ -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>
+36
View File
@@ -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());
});
}
}
+28
View File
@@ -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
+128
View File
@@ -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;
}
+71
View File
@@ -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
+189
View File
@@ -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;
}
+62
View File
@@ -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
+35
View File
@@ -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
+67
View File
@@ -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"
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

+288
View File
@@ -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 }
*/
+111
View File
@@ -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.
*/
+9
View File
@@ -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()
+35
View File
@@ -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"
)
+525
View File
@@ -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();
}
+109
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{ "Keys": [ "jsonviewer" ] }
+39
View File
@@ -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"
)
+212
View File
@@ -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;
}
}
+69
View File
@@ -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
+266
View File
@@ -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 &current, 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();
}
+76
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{ "Keys": [ "pdfviewer" ] }
+64
View File
@@ -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);
}
}
+30
View File
@@ -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
+35
View File
@@ -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"
)
+196
View File
@@ -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]
+50
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
{ "Keys": [ "txtviewer" ] }
Executable
+13
View File
@@ -0,0 +1,13 @@
#!/bin/sh
rm -rf build/
mkdir -p build/
cd build
# Build the project
cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$HOME/Qt/6.6.1/gcc_64/ \
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..
make
cp ./compile_commands.json ../