Migrate documentviewer example into plugin based architecture

This patch changes the document viewer example to a plugin based
architecture.
It adapts the documentation accordingly.

Change-Id: Ia24028aa27e21fb8ab36f5ef3a9953be60858b19
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Axel Spoerl 2023-03-23 16:25:43 +01:00
parent 58d456c89e
commit ba61af0bf3
72 changed files with 1162 additions and 236 deletions

View File

@ -23,6 +23,6 @@ endif()
if(TARGET Qt::Quick AND TARGET Qt::qsb AND TARGET Qt::Pdf) if(TARGET Qt::Quick AND TARGET Qt::qsb AND TARGET Qt::Pdf)
qt_internal_add_example(photosurface) qt_internal_add_example(photosurface)
endif() endif()
if(TARGET Qt::Widgets AND TARGET Qt::PdfWidgets) if(TARGET Qt::Widgets)
qt_internal_add_example(documentviewer) qt_internal_add_example(documentviewer)
endif() endif()

View File

@ -11,7 +11,7 @@ endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/demos/${PROJECT_NAME}") set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/demos/${PROJECT_NAME}")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick)
qt_standard_project_setup(REQUIRES 6.5) qt_standard_project_setup()
qt_add_executable(colorpaletteclient qt_add_executable(colorpaletteclient
main.cpp main.cpp

View File

@ -1,93 +1,21 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
project(documentviewer LANGUAGES CXX) project(documentviewer LANGUAGES CXX)
set(CMAKE_AUTOMOC ON) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets
OPTIONAL_COMPONENTS PrintSupport PdfWidgets)
if(NOT DEFINED INSTALL_EXAMPLESDIR) if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples") set(INSTALL_EXAMPLESDIR "examples")
endif() endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets PdfWidgets
OPTIONAL_COMPONENTS PrintSupport)
qt_standard_project_setup()
qt_add_executable(documentviewer
main.cpp
mainwindow.cpp mainwindow.h mainwindow.ui
abstractviewer.cpp abstractviewer.h
viewerfactory.cpp viewerfactory.h
jsonviewer.cpp jsonviewer.h
txtviewer.cpp txtviewer.h
pdfviewer.cpp pdfviewer.h
zoomselector.cpp zoomselector.h
hoverwatcher.cpp hoverwatcher.cpp
)
set_target_properties(documentviewer PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
# Resources:
set(documentviewer_resource_files
"images/copy@2x.png"
"images/copy.png"
"images/cut@2x.png"
"images/cut.png"
"images/go-next-view@2x.png"
"images/go-next-view-page@2x.png"
"images/go-next-view-page.png"
"images/go-next-view.png"
"images/go-previous-view@2x.png"
"images/go-previous-view-page@2x.png"
"images/go-previous-view-page.png"
"images/go-previous-view.png"
"images/magnifier@2x.png"
"images/magnifier.png"
"images/open@2x.png"
"images/open.png"
"images/paste@2x.png"
"images/paste.png"
"images/print2x.png"
"images/print.png"
"images/qt-logo@2x.png"
"images/qt-logo.png"
"images/zoom-fit-best@2x.png"
"images/zoom-fit-best.png"
"images/zoom-fit-width@2x.png"
"images/zoom-fit-width.png"
"images/zoom-in@2x.png"
"images/zoom-in.png"
"images/zoom-original@2x.png"
"images/zoom-original.png"
"images/zoom-out@2x.png"
"images/zoom-out.png"
"images/zoom-previous@2x.png"
"images/zoom-previous.png"
)
qt6_add_resources(documentviewer "documentviewer"
PREFIX
"/demos/documentviewer"
FILES
${documentviewer_resource_files}
)
target_link_libraries(documentviewer PUBLIC
Qt::Core
Qt::Gui
Qt::Widgets
Qt::PdfWidgets
)
if(TARGET Qt6::PrintSupport) if(TARGET Qt6::PrintSupport)
target_link_libraries(documentviewer PRIVATE Qt6::PrintSupport) add_compile_definitions(QT_DOCUMENTVIEWER_PRINTSUPPORT)
endif() endif()
install(TARGETS documentviewer set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/demos/documentviewer")
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}" add_subdirectory(app)
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}" add_subdirectory(plugins)
)

View File

@ -0,0 +1,96 @@
cmake_minimum_required(VERSION 3.16)
project(documentviewer LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets
OPTIONAL_COMPONENTS PrintSupport PdfWidgets)
qt_standard_project_setup()
set(CMAKE_AUTOMOC ON)
qt_add_executable(documentviewer
main.cpp
mainwindow.cpp mainwindow.h mainwindow.ui
viewerfactory.cpp viewerfactory.h
abstractviewer.cpp abstractviewer.h
recentfiles.cpp recentfiles.h
recentfilemenu.cpp recentfilemenu.h
viewerinterfaces.h
)
set_target_properties(documentviewer PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
qt_add_resources(documentviewer "documentviewer"
PREFIX
"/demos/documentviewer"
FILES
"images/copy@2x.png"
"images/copy.png"
"images/cut@2x.png"
"images/cut.png"
"images/go-next-view@2x.png"
"images/go-next-view-page@2x.png"
"images/go-next-view-page.png"
"images/go-next-view.png"
"images/go-previous-view@2x.png"
"images/go-previous-view-page@2x.png"
"images/go-previous-view-page.png"
"images/go-previous-view.png"
"images/magnifier@2x.png"
"images/magnifier.png"
"images/open@2x.png"
"images/open.png"
"images/paste@2x.png"
"images/paste.png"
"images/print2x.png"
"images/print.png"
"images/qt-logo@2x.png"
"images/qt-logo.png"
"images/zoom-fit-best@2x.png"
"images/zoom-fit-best.png"
"images/zoom-fit-width@2x.png"
"images/zoom-fit-width.png"
"images/zoom-in@2x.png"
"images/zoom-in.png"
"images/zoom-original@2x.png"
"images/zoom-original.png"
"images/zoom-out@2x.png"
"images/zoom-out.png"
"images/zoom-previous@2x.png"
"images/zoom-previous.png"
)
target_link_libraries(documentviewer PRIVATE
Qt::Core
Qt::Gui
Qt::Widgets
)
if(QT6_IS_SHARED_LIBS_BUILD)
add_dependencies(documentviewer
jsonviewer
pdfviewer
txtviewer
)
else()
target_link_libraries(documentviewer PRIVATE
jsonviewer
pdfviewer
txtviewer
)
endif()
if(TARGET Qt6::PrintSupport)
target_link_libraries(documentviewer 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}"
)

View File

@ -13,14 +13,18 @@
#include <QSettings> #include <QSettings>
#include <QApplication> #include <QApplication>
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#include <QPrintDialog> #include <QPrintDialog>
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
AbstractViewer::AbstractViewer(QFile *file, QWidget *widget, QMainWindow *mainWindow) : AbstractViewer::AbstractViewer() : m_file(nullptr), m_widget(nullptr)
m_file(file),
m_widget(widget)
{ {
}
void AbstractViewer::init(QFile *file, QWidget *widget, QMainWindow *mainWindow)
{
m_file.reset(file);
m_widget = widget;
Q_ASSERT(widget); Q_ASSERT(widget);
Q_ASSERT(mainWindow); Q_ASSERT(mainWindow);
m_uiAssets.mainWindow = mainWindow; m_uiAssets.mainWindow = mainWindow;
@ -35,6 +39,96 @@ AbstractViewer::~AbstractViewer()
qDeleteAll(m_toolBars); qDeleteAll(m_toolBars);
} }
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) void AbstractViewer::statusMessage(const QString &message, const QString &type, int timeout)
{ {
const QString msg = viewerName() + (type.isEmpty() ? ": " : "/" + type + ": ") + message; const QString msg = viewerName() + (type.isEmpty() ? ": " : "/" + type + ": ") + message;
@ -78,7 +172,7 @@ QMenu *AbstractViewer::fileMenu()
void AbstractViewer::print() void AbstractViewer::print()
{ {
#ifdef QT_ABSTRACTVIEWER_PRINTSUPPORT #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
static const QString type = tr("Printing"); static const QString type = tr("Printing");
if (!hasContent()) { if (!hasContent()) {
statusMessage(tr("No content to print."), type); statusMessage(tr("No content to print."), type);
@ -126,7 +220,7 @@ void AbstractViewer::print()
*/ */
void AbstractViewer::maybeSetPrintingEnabled(bool enabled) void AbstractViewer::maybeSetPrintingEnabled(bool enabled)
{ {
#ifndef QT_ABSTRACTVIEWER_PRINTSUPPORT #ifndef QT_DOCUMENTVIEWER_PRINTSUPPORT
enabled = false; enabled = false;
#else #else
if (!hasContent()) if (!hasContent())

View File

@ -8,14 +8,9 @@
#include <QMainWindow> #include <QMainWindow>
#include <QFileInfo> #include <QFileInfo>
#if defined(QT_PRINTSUPPORT_LIB) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#include <QtPrintSupport/qtprintsupportglobal.h> #include <QtPrintSupport/qtprintsupportglobal.h>
#if QT_CONFIG(printer)
#if QT_CONFIG(printdialog)
#define QT_ABSTRACTVIEWER_PRINTSUPPORT
#include <QPrinter> #include <QPrinter>
#endif // QT_CONFIG(printdialog)
#endif // QT_CONFIG(printer)
#endif // QT_PRINTSUPPORT_LIB #endif // QT_PRINTSUPPORT_LIB
class QToolBar; class QToolBar;
@ -27,28 +22,32 @@ class AbstractViewer : public QObject
Q_OBJECT Q_OBJECT
protected: protected:
explicit AbstractViewer(QFile *file, QWidget *widget, QMainWindow *mainWindow); AbstractViewer();
public: public:
virtual ~AbstractViewer(); virtual ~AbstractViewer();
virtual void init(QFile *file, QWidget *widget, QMainWindow *mainWindow);
void initViewer(QAction *back, QAction *forward, QAction *help, QTabWidget *tabs); void initViewer(QAction *back, QAction *forward, QAction *help, QTabWidget *tabs);
virtual bool isModified() const { return false; } virtual bool isModified() const;
virtual bool saveDocument() { return false; } virtual bool saveDocument();
virtual bool saveDocumentAs() { return false; } virtual bool saveDocumentAs();
virtual QString viewerName() const = 0; virtual QString viewerName() const = 0;
virtual bool supportsOverview() const { return false; } virtual bool supportsOverview() const;
virtual QByteArray saveState() const = 0; virtual QByteArray saveState() const = 0;
virtual bool restoreState(QByteArray &) = 0; virtual bool restoreState(QByteArray &) = 0;
virtual bool hasContent() const { return false; } virtual bool hasContent() const;
bool isEmpty() const { return !hasContent(); } virtual QStringList supportedMimeTypes() const = 0;
bool isPrintingEnabled() const { return m_printingEnabled; } virtual bool isDefaultViewer() const;
bool isEmpty() const;
bool isPrintingEnabled() const;
AbstractViewer *viewer();
const AbstractViewer *viewer() const;
QList<QAction *> actions() const { return m_actions; } QList<QAction *> actions() const;
QWidget *widget() const { return m_widget; } QWidget *widget() const;
QList<QMenu *> menus() const { return m_menus; } QList<QMenu *> menus() const;
#ifdef QT_ABSTRACTVIEWER_PRINTSUPPORT #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
protected: protected:
virtual void printDocument(QPrinter *) const {}; virtual void printDocument(QPrinter *) const {};
#endif #endif
@ -76,18 +75,18 @@ protected:
QToolBar *addToolBar(const QString &); QToolBar *addToolBar(const QString &);
QMenu *addMenu(const QString &); QMenu *addMenu(const QString &);
QMenu *fileMenu(); QMenu *fileMenu();
QMainWindow *mainWindow() const { return m_uiAssets.mainWindow; } QMainWindow *mainWindow() const;
QStatusBar *statusBar() const { return mainWindow()->statusBar(); } QStatusBar *statusBar() const;
QMenuBar *menuBar() const { return mainWindow()->menuBar(); } QMenuBar *menuBar() const;
std::unique_ptr<QFile> m_file; std::unique_ptr<QFile> m_file;
QList<QAction *> m_actions; QList<QAction *> m_actions;
QWidget *m_widget; QWidget *m_widget = nullptr;
protected slots: protected slots:
void maybeSetPrintingEnabled(bool enabled); void maybeSetPrintingEnabled(bool enabled);
inline void maybeEnablePrinting() { return maybeSetPrintingEnabled(true); } void maybeEnablePrinting();
inline void disablePrinting() { return maybeSetPrintingEnabled(false); } void disablePrinting();
private: private:
QList<QMenu *> m_menus; QList<QMenu *> m_menus;

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 990 B

After

Width:  |  Height:  |  Size: 990 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 1010 B

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1006 B

After

Width:  |  Height:  |  Size: 1006 B

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1017 B

After

Width:  |  Height:  |  Size: 1017 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 678 B

After

Width:  |  Height:  |  Size: 678 B

View File

Before

Width:  |  Height:  |  Size: 931 B

After

Width:  |  Height:  |  Size: 931 B

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 905 B

After

Width:  |  Height:  |  Size: 905 B

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 952 B

After

Width:  |  Height:  |  Size: 952 B

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

Before

Width:  |  Height:  |  Size: 946 B

After

Width:  |  Height:  |  Size: 946 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 940 B

After

Width:  |  Height:  |  Size: 940 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 946 B

After

Width:  |  Height:  |  Size: 946 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,9 +1,12 @@
// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "abstractviewer.h"
#include "viewerfactory.h" #include "viewerfactory.h"
#include "abstractviewer.h"
#include "recentfiles.h"
#include "recentfilemenu.h"
#include <QFileDialog> #include <QFileDialog>
#include <QSettings> #include <QSettings>
#include <QToolButton> #include <QToolButton>
@ -15,7 +18,25 @@ MainWindow::MainWindow(QWidget *parent) :
ui(new Ui::MainWindow) ui(new Ui::MainWindow)
{ {
ui->setupUi(this); ui->setupUi(this);
m_recentFiles.reset(new RecentFiles(ui->actionRecent));
connect(m_recentFiles.get(), &RecentFiles::countChanged, this, [&](int count){
ui->actionRecent->setText(QString("%1 recent files").arg(count));
});
readSettings(); readSettings();
m_factory.reset(new ViewerFactory(ui->viewArea, this));
const QStringList &viewers = m_factory->viewerNames();
const QString msg = tr("Available viewers: ") + viewers.join(", ");
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() MainWindow::~MainWindow()
@ -46,24 +67,48 @@ void MainWindow::openFile(const QString &fileName)
QFileInfo fileInfo(*file); QFileInfo fileInfo(*file);
m_currentDir = fileInfo.dir(); m_currentDir = fileInfo.dir();
m_recentFiles->addFile(fileInfo.absoluteFilePath());
// If a viewer is already open, save its state first // If a viewer is already open, save its state first
saveViewerSettings(); saveViewerSettings();
m_viewer.reset(ViewerFactory::makeViewer(file, ui->viewArea, this)); m_viewer = m_factory->viewer(file);
restoreViewerSettings(); if (!m_viewer) {
statusBar()->showMessage(tr("File %1 can't be opened.").arg(fileName));
return;
}
ui->actionPrint->setEnabled(m_viewer->hasContent()); ui->actionPrint->setEnabled(m_viewer->hasContent());
connect(m_viewer.get(), &AbstractViewer::printingEnabledChanged, ui->actionPrint, &QAction::setEnabled); connect(m_viewer, &AbstractViewer::printingEnabledChanged, ui->actionPrint, &QAction::setEnabled);
connect(ui->actionPrint, &QAction::triggered, m_viewer.get(), &AbstractViewer::print); connect(ui->actionPrint, &QAction::triggered, m_viewer, &AbstractViewer::print);
connect(m_viewer.get(), &AbstractViewer::showMessage, statusBar(), &QStatusBar::showMessage); connect(m_viewer, &AbstractViewer::showMessage, statusBar(), &QStatusBar::showMessage);
m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget); m_viewer->initViewer(ui->actionBack, ui->actionForward, ui->menuHelp->menuAction(), ui->tabWidget);
restoreViewerSettings();
ui->scrollArea->setWidget(m_viewer->widget()); ui->scrollArea->setWidget(m_viewer->widget());
} }
void MainWindow::on_actionAbout_triggered() void MainWindow::on_actionAbout_triggered()
{ {
QMessageBox::aboutQt(this); static const QString &text = [&]{
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") +
m_factory->viewerNames().join(", ") +
tr("\n\nIt supports thow following mime types:\n") +
m_factory->supportedMimeTypes().join("\n");
AbstractViewer *def = m_factory->defaultViewer();
if (def) {
text += tr("\n\nOther mime types will be displayed with ") + def->viewerName() + ".";
}
return text;
}();
QMessageBox::about(this, tr("About Document Viewer Demo"), text);
} }
void MainWindow::on_actionAboutQt_triggered() void MainWindow::on_actionAboutQt_triggered()
@ -86,6 +131,9 @@ void MainWindow::readSettings()
QByteArray mainWindowState = settings.value(settingsMainWindow).toByteArray(); QByteArray mainWindowState = settings.value(settingsMainWindow).toByteArray();
restoreState(mainWindowState); restoreState(mainWindowState);
} }
// Restore recent files
m_recentFiles->restoreFromSettings(settings, settingsFiles);
} }
void MainWindow::saveSettings() const void MainWindow::saveSettings() const
@ -98,6 +146,9 @@ void MainWindow::saveSettings() const
// Save QMainWindow state // Save QMainWindow state
settings.setValue(settingsMainWindow, saveState()); settings.setValue(settingsMainWindow, saveState());
// Save recent files
m_recentFiles->saveSettings(settings, settingsFiles);
settings.sync(); settings.sync();
} }

View File

@ -10,6 +10,8 @@
#include <QStringLiteral> #include <QStringLiteral>
class AbstractViewer; class AbstractViewer;
class RecentFiles;
class ViewerFactory;
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
@ -37,14 +39,17 @@ private:
void saveViewerSettings() const; void saveViewerSettings() const;
QDir m_currentDir; QDir m_currentDir;
AbstractViewer *m_viewer = nullptr;
std::unique_ptr<Ui::MainWindow> ui; std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<AbstractViewer> m_viewer; std::unique_ptr<RecentFiles> m_recentFiles;
int m_classId = -1; int m_classId = -1;
std::unique_ptr<ViewerFactory> m_factory;
static constexpr QLatin1StringView settingsName = QLatin1StringView("DocumentViewerExample"); static constexpr QLatin1StringView settingsName = QLatin1StringView("DocumentViewerExample");
static constexpr QLatin1StringView settingsDir = QLatin1StringView("WorkingDir"); static constexpr QLatin1StringView settingsDir = QLatin1StringView("WorkingDir");
static constexpr QLatin1StringView settingsMainWindow = QLatin1StringView("MainWindow"); static constexpr QLatin1StringView settingsMainWindow = QLatin1StringView("MainWindow");
static constexpr QLatin1StringView settingsViewers = QLatin1StringView("Viewers"); static constexpr QLatin1StringView settingsViewers = QLatin1StringView("Viewers");
static constexpr QLatin1StringView settingsFiles = QLatin1StringView("RecentFiles");
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View File

@ -14,7 +14,7 @@
<string>Document Viewer Demo</string> <string>Document Viewer Demo</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset resource="../../../../../build/dev/qtdoc/examples/demos/documentviewer/.rcc/documentviewer.qrc"> <iconset resource="../../../../../../build/dev/qtdoc/examples/demos/documentviewer/app/.rcc/documentviewer.qrc">
<normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff>:/demos/documentviewer/images/qt-logo.png</iconset> <normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff>:/demos/documentviewer/images/qt-logo.png</iconset>
</property> </property>
<widget class="QWidget" name="centralwidget"> <widget class="QWidget" name="centralwidget">
@ -96,6 +96,7 @@
<string>File</string> <string>File</string>
</property> </property>
<addaction name="actionOpen"/> <addaction name="actionOpen"/>
<addaction name="actionRecent"/>
<addaction name="actionPrint"/> <addaction name="actionPrint"/>
<addaction name="actionQuit"/> <addaction name="actionQuit"/>
</widget> </widget>
@ -121,6 +122,7 @@
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="actionOpen"/> <addaction name="actionOpen"/>
<addaction name="actionRecent"/>
<addaction name="actionPrint"/> <addaction name="actionPrint"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionBack"/> <addaction name="actionBack"/>
@ -141,8 +143,8 @@
</action> </action>
<action name="actionAbout"> <action name="actionAbout">
<property name="icon"> <property name="icon">
<iconset theme="help-about"> <iconset theme="help-about" resource="../../../../../../build/dev/qtdoc/examples/demos/documentviewer/app/.rcc/documentviewer.qrc">
<normaloff>.</normaloff>.</iconset> <normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff>:/demos/documentviewer/images/qt-logo.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>about documentviewer</string> <string>about documentviewer</string>
@ -189,8 +191,8 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="icon"> <property name="icon">
<iconset theme="document-print"> <iconset theme="document-print" resource="../../../../../../build/dev/qtdoc/examples/demos/documentviewer/app/.rcc/documentviewer.qrc">
<normaloff>:/demos/documentviewer/images/print.png</normaloff>:/demos/documentviewer/images/print.png</iconset> <normaloff>:/demos/documentviewer/images/print.png</normaloff>:/demos/documentviewer/images/print.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Print</string> <string>Print</string>
@ -204,9 +206,9 @@
</action> </action>
<action name="actionAboutQt"> <action name="actionAboutQt">
<property name="icon"> <property name="icon">
<iconset resource="../../../../../build/dev/qthttpserver/examples/httpserver/simple/.rcc/assets.qrc"> <iconset resource="../../../../../../build/dev/qtdoc/examples/demos/documentviewer/app/.rcc/documentviewer.qrc">
<normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff> <normaloff>:/demos/documentviewer/images/qt-logo.png</normaloff>
<normalon>:/demos/documentviewer/images/qt-logo.png</normalon>:/demos/documentviewer/images/qt-logo.png</iconset> <normalon>:/demos/documentviewer/images/qt-logo.png</normalon>:/demos/documentviewer/images/qt-logo.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>About Qt</string> <string>About Qt</string>
@ -224,7 +226,7 @@
<normaloff>.</normaloff>.</iconset> <normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Open</string> <string>Recently opened...</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string>Meta+R</string> <string>Meta+R</string>
@ -232,7 +234,8 @@
</action> </action>
<action name="actionQuit"> <action name="actionQuit">
<property name="icon"> <property name="icon">
<iconset theme="application-exit"/> <iconset theme="application-exit">
<normaloff>.</normaloff>.</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Quit</string> <string>Quit</string>
@ -246,8 +249,7 @@
</action> </action>
</widget> </widget>
<resources> <resources>
<include location="../../../../../build/dev/qtdoc/examples/demos/documentviewer/.rcc/documentviewer.qrc"/> <include location="../../../../../../build/dev/qtdoc/examples/demos/documentviewer/app/.rcc/documentviewer.qrc"/>
<include location="../../../../../build/dev/qthttpserver/examples/httpserver/simple/.rcc/assets.qrc"/>
</resources> </resources>
<connections> <connections>
<connection> <connection>

View File

@ -0,0 +1,38 @@
// 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>
#include <QObject>
#include <QWidget>
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());
});
}
}

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 QAction;
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

View File

@ -0,0 +1,169 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "recentfiles.h"
#include <QFileInfo>
#include <QSettings>
RecentFiles::RecentFiles(QObject *parent) : QObject(parent)
{
}
void RecentFiles::clear()
{
if (isEmpty())
return;
QStringList::clear();
emit countChanged(0);
emit listCleared();
}
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 = count();
// Clean dangling and duplicate files
bool duplicateFound = false;
for (const QString &file : *this) {
if (!testFileAccess(file)) {
removeFile(file, RemoveReason::Dangling);
} else if (file == fileName) {
removeFile(file, RemoveReason::Duplicate);
duplicateFound = true;
}
}
// Cut tail
while (count() > m_maxFiles)
removeFile((count() - 1), RemoveReason::TailCut);
prepend(fileName);
switch (policy) {
case EmitPolicy::NeverEmit:
return;
case EmitPolicy::AllwaysEmit:
emit changed();
emit fileAdded(fileName);
emit countChanged(count());
return;
case EmitPolicy::EmitWhenChanged:
emit changed();
if (!duplicateFound)
emit fileAdded(fileName);
if (c != count())
emit countChanged(count());
return;
}
}
void RecentFiles::addFiles(const QStringList &files)
{
if (files.isEmpty())
return;
if (files.count() == 1) {
addFile(files.at(0));
return;
}
const auto &c = count();
for (const auto &file : files)
addFile(file, EmitPolicy::NeverEmit);
emit filesAdded(files);
emit changed();
if (count() != c)
emit countChanged(count());
}
// Test if file exists and can be opened
bool RecentFiles::testFileAccess(const QString &fileName) const
{
QFileInfo info(fileName);
if (!info.isFile())
return false;
switch (m_openMode) {
case QIODevice::ReadOnly:
if (!info.isReadable())
return false;
break;
case QIODevice::ReadWrite:
if (!(info.isReadable() && info.isWritable()))
return false;
break;
case QIODevice::WriteOnly:
if (!info.isWritable())
return false;
break;
}
return true;
}
void RecentFiles::removeFile(qsizetype index, RemoveReason reason)
{
if (index < 0 || index >= count())
return;
const QString &fileName = at(index);
remove(index);
// No emit for duplicate removal, add emits changed later.
if (reason == RemoveReason::Duplicate)
return;
emit fileRemoved(fileName, reason);
emit changed();
}
void RecentFiles::saveSettings(QSettings &settings, const QString &key) const
{
settings.beginGroup(key);
settings.setValue(s_maxFiles, maxFiles());
settings.setValue(s_openMode, static_cast<int>(openMode()));
if (!isEmpty()) {
settings.beginWriteArray(s_fileNames, count());
for (int index = 0; index < count(); ++index) {
settings.setArrayIndex(index);
settings.setValue(s_file, 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();
const auto mode = qvariant_cast<QIODevice::OpenMode>(settings.value(s_openMode,
static_cast<int>(openMode())).toInt());
setMaxFiles(mxFiles);
setOpenMode(mode);
QStringList::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 (count() > 0) {
emit settingsRestored(count());
emit changed();
}
return true;
}

View File

@ -0,0 +1,81 @@
// 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>
class QSettings;
class RecentFiles : public QObject, private QStringList
{
Q_OBJECT
public:
enum class RemoveReason {
Reset,
Dangling,
TailCut,
RemovedManually,
Duplicate
};
Q_ENUM(RemoveReason)
explicit RecentFiles(QObject *parent);
// Access to QStringList member functions
qsizetype count() const {return QStringList::count(); };
const QStringList recentFiles() const {return *this; }
bool isEmpty() const { return QStringList::isEmpty(); }
// Properties
qsizetype maxFiles() const { return m_maxFiles; }
void setMaxFiles(qsizetype maxFiles) { m_maxFiles = maxFiles; }
QIODevice::OpenMode openMode() const { return m_openMode; }
void setOpenMode(QIODevice::OpenMode mode) { m_openMode = mode; }
public slots:
void addFile(const QString &fileName) {addFile(fileName, EmitPolicy::EmitWhenChanged); }
void addFiles(const QStringList &fileNames);
void removeFile(const QString &fileName) {removeFile(indexOf(fileName)); }
void removeFile(qsizetype index) {removeFile(index, RemoveReason::RemovedManually); }
void saveSettings(QSettings &settings, const QString &key) const;
bool restoreFromSettings(QSettings &settings, const QString &key);
void clear();
signals:
void fileAdded(const QString &fileName);
void filesAdded(const QStringList &files);
void fileRemoved(const QString &fileName, RecentFiles::RemoveReason reason);
void countChanged(int count);
void listCleared();
void settingsRestored(qsizetype count);
void changed();
private:
bool testFileAccess(const QString &fileName) const;
// Private removers with reason
void removeFile(qsizetype index, RemoveReason reason);
void removeFile(const QString &fileName, RemoveReason reason) {removeFile(indexOf(fileName), reason); }
// Private adder with emit policy
enum class EmitPolicy {
AllwaysEmit,
EmitWhenChanged,
NeverEmit
};
void addFile(const QString &fileName, EmitPolicy policy);
qsizetype m_maxFiles = 10;
QIODevice::OpenMode m_openMode = QIODevice::ReadOnly;
// 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

View File

@ -0,0 +1,173 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QWidget>
#include <QMimeDatabase>
#include <QMimeType>
#include <QApplication>
#include <QPluginLoader>
#include <QDir>
#include "abstractviewer.h"
#include "viewerfactory.h"
#include "viewerinterfaces.h"
#include <concepts>
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_WIN)
if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
pluginsDir.cdUp();
#elif defined(Q_OS_DARWIN)
if (pluginsDir.dirName() == "macOS") {
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")) {
name += "(default)";
}
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;
}
}
return defaultViewer();
}
AbstractViewer *ViewerFactory::defaultViewer() const
{
switch (m_defaultPolicy) {
case DefaultPolicy::NeverDefault:
return nullptr;
case DefaultPolicy::DefaultToCustomViewer:
return m_defaultViewer ? m_defaultViewer : findViewer("TxtViewer");
case DefaultPolicy::DefaultToTxtViewer:
return findViewer("TxtViewer");
}
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;
}

View File

@ -0,0 +1,57 @@
// 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 <QMimeType>
#include <QObject>
#include <QMap>
class AbstractViewer;
class QWidget;
class QMainWindow;
class Questions;
class QFile;
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; }
AbstractViewer *viewer(QFile *file) const;
typedef QMap<QString, AbstractViewer *> ViewerMap;
typedef QList<AbstractViewer *> ViewerList;
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;
void loadViewerPlugins();
void addViewer(QObject *viewerObject);
AbstractViewer *viewer(const QMimeType &type) const;
void unload();
};
#endif // VIEWERFACTORY_H

View File

@ -0,0 +1,39 @@
// 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>
#include <QWidget>
QT_BEGIN_NAMESPACE
class QFile;
class QMainWindow;
class QWidget;
class QTabWidget;
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

View File

@ -27,14 +27,14 @@
To create an executable, we use a standard main.cpp file. To create an executable, we use a standard main.cpp file.
First, we set the application's organization name: First, we set the application's organization name:
\quotefromfile demos/documentviewer/main.cpp \quotefromfile demos/documentviewer/app/main.cpp
\skipto int main \skipto int main
\printuntil QApplication::set \printuntil QApplication::set
\endsection1 \endsection1
\section1 Creating an application and showing the main window \section1 Creating an application and showing the main window
\quotefromfile demos/documentviewer/main.cpp \quotefromfile demos/documentviewer/app/main.cpp
\skipto QApplication app \skipto QApplication app
\printuntil } \printuntil }
\endsection1 \endsection1
@ -44,13 +44,17 @@
The class constructor initializes the user interface created in Qt Designer. The class constructor initializes the user interface created in Qt Designer.
It links the actions for opening a file and the about dialog to their implementation. It links the actions for opening a file and the about dialog to their implementation.
\quotefromfile demos/documentviewer/mainwindow.cpp \quotefromfile demos/documentviewer/app/mainwindow.cpp
\skipto ui->setupUi \skipto ui->setupUi
\printuntil triggered \printuntil triggered
The \c mainwindow.ui file provides a QTabWidget on the left, where bookmarks and thumbnails The \c mainwindow.ui file provides a QTabWidget on the left, where bookmarks and thumbnails
can be displayed. It provides a QScrollArea on the right, where the viewed file contents are can be displayed. It provides a QScrollArea on the right, where the viewed file contents are
displayed. \printuntil showMenu);
If a file is opened, the {File} class (inheriting from {QFile}) automatically determines
the FileType used in this application. If no valid file type could be established, the user
is asked, if it should be opened as plain text.
The ViewerFactory class provides a static method to create a file type specific viewer. The ViewerFactory class provides a static method to create a file type specific viewer.
@ -62,7 +66,7 @@
virtual restoreState method. Afterwards, the standard UI assets are passed to the viewer virtual restoreState method. Afterwards, the standard UI assets are passed to the viewer
and it's display widget is displayed in the main scroll area. and it's display widget is displayed in the main scroll area.
\quotefromfile demos/documentviewer/mainwindow.cpp \quotefromfile demos/documentviewer/app/mainwindow.cpp
\skipuntil void MainWindow::openFile \skipuntil void MainWindow::openFile
\skipto m_viewer->initViewer \skipto m_viewer->initViewer
\printuntil } \printuntil }
@ -74,7 +78,7 @@
displayed, the main window and the user questions. displayed, the main window and the user questions.
Depending on the file's mime type, it creates an appropriate document viewer. Depending on the file's mime type, it creates an appropriate document viewer.
\quotefromfile demos/documentviewer/mainwindow.cpp \quotefromfile demos/documentviewer/app/mainwindow.cpp
\skipto AbstractViewer \skipto AbstractViewer
\printuntil } \printuntil }
\endsection1 \endsection1
@ -159,40 +163,40 @@
HoverWatcher inherits from QObject and the QWidget watched becomes the instance's parent. HoverWatcher inherits from QObject and the QWidget watched becomes the instance's parent.
An event filter is used to intercept the hover events without consuming them. An event filter is used to intercept the hover events without consuming them.
\quotefromfile demos/documentviewer/hoverwatcher.cpp \quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.cpp
\skipto HoverWatcher::HoverWatcher \skipto HoverWatcher::HoverWatcher
\printuntil } \printuntil }
The actions watched are represented in an enum. The actions watched are represented in an enum.
\quotefromfile demos/documentviewer/hoverwatcher.h \quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
\skipto enum HoverAction \skipto enum HoverAction
\printuntil }; \printuntil };
Static methods create watchers, check their existence for a specific QWidget or dismiss Static methods create watchers, check their existence for a specific QWidget or dismiss
a watcher. a watcher.
\quotefromfile demos/documentviewer/hoverwatcher.h \quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
\skipto static HoverWatcher \skipto static HoverWatcher
\printuntil static void dismiss \printuntil static void dismiss
A cursor shape can be specified or unset for each HoverAction. If no cursor shape is A cursor shape can be specified or unset for each HoverAction. If no cursor shape is
specified for an action, the application's override cursor will be restored when it occurs. specified for an action, the application's override cursor will be restored when it occurs.
\quotefromfile demos/documentviewer/hoverwatcher.h \quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
\skipto public slots \skipto public slots
\printuntil void unSetCursorShape \printuntil void unSetCursorShape
The mouseButtons property specifies, which mouse buttons to consider for the MousePress action. The mouseButtons property specifies, which mouse buttons to consider for the MousePress action.
\quotefromfile demos/documentviewer/hoverwatcher.h \quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
\skipuntil public slots \skipuntil public slots
\skipto setMouseButtons \skipto setMouseButtons
\printuntil setMouseButton( \printuntil setMouseButton(
Action specific signals are emitted when an action has been processed. Action specific signals are emitted when an action has been processed.
\quotefromfile demos/documentviewer/hoverwatcher.h \quotefromfile demos/documentviewer/plugins/pdfviewer/hoverwatcher.h
\skipto signals \skipto signals
\printuntil left(); \printuntil left();
@ -201,6 +205,72 @@
\code \code
void hoverAction(HoverAction action); void hoverAction(HoverAction action);
\endcode \endcode
\endsection2
\section2 The {RecentFiles} class
The class is a QStringList, specialized to manage a list of recently opened files.
\quotefromfile demos/documentviewer/recentfiles.cpp
\skipto RecentFiles::RecentFiles
\printuntil }
It provides slots to add a single or multiple files, by passing their path or a list
of paths as an argument. A file is added, if the path leads to a valid file.
If a file already exists in the list of recent files, it is removed from its original position
and added on the top.
\quotefromfile demos/documentviewer/recentfiles.h
\skipto public slots
\printuntil addFiles
Files can be removed by name or by index.
\quotefromfile demos/documentviewer/recentfiles.h
\skipuntil public slots
\skipto removeFile
\printuntil addFiles
Slots are available to save and restore from {QSettings}.
Files restored from settings are ignored, if they no longer exist.
\quotefromfile demos/documentviewer/recentfiles.h
\skipuntil public slots
\skipto saveSettings
\printuntil restoreFromSettings
The maxFiles property manages the maximum amount of recent files to be remebered (default: 10).
\code
qsizetype maxFiles();
void setMaxFiles(qsizetype maxFiles);
\endcode
{RecentFiles} verifies a file can be opened in {openMode}, before adding it.
\code
void setOpenMode(QIODevice::OpenMode mode);
QIODevice::OpenMode openMode() const;
\endcode
\endsection2
\section2 The {RecentFileMenu} class
The {RecentFileMenu} class is a QMenu, specialized as a submenu to display a \l{RecentFiles}
object as a submenu.
Its constructor takes a parent widget, usually and QAction or a QMenu.
Its fileOpened signal passes a QString argument with the absolute file name, when the
application user has selected a recent file from the list.
\note
A {RecentFileMenu} is deleted either with its parent widget, or with the RecentFiles object
passed to its constructor.
\quotefromfile demos/documentviewer/recentfilemenu.h
\skipuntil class RecentFileMenu
\printuntil void fileOpened
\endsection2 \endsection2
\endsection1 \endsection1

View File

@ -0,0 +1,6 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(jsonviewer)
add_subdirectory(pdfviewer)
add_subdirectory(txtviewer)

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
../../app/abstractviewer.h ../../app/abstractviewer.cpp
)
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
)
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"
)

View File

@ -18,14 +18,14 @@
#include <QLineEdit> #include <QLineEdit>
#include <QLabel> #include <QLabel>
#include <QApplication> #include <QApplication>
#ifdef QT_ABSTRACTVIEWER_PRINTSUPPORT #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#include <QPrinter> #include <QPrinter>
#include <QPainter> #include <QPainter>
#endif #endif
JsonViewer::JsonViewer(QFile *file, QWidget *parent, QMainWindow *mainWindow) : void JsonViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
AbstractViewer(file, new QTreeView(parent), mainWindow)
{ {
AbstractViewer::init(file, new QTreeView(parent), mainWindow);
m_tree = qobject_cast<QTreeView *>(widget()); m_tree = qobject_cast<QTreeView *>(widget());
connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi); connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi);
} }
@ -35,6 +35,11 @@ JsonViewer::~JsonViewer()
delete m_toplevel; delete m_toplevel;
} }
QStringList JsonViewer::supportedMimeTypes() const
{
return {"application/json"};
}
void JsonViewer::setupJsonUi() void JsonViewer::setupJsonUi()
{ {
// Build Menus and toolbars // Build Menus and toolbars
@ -268,7 +273,7 @@ bool JsonViewer::hasContent() const
return !m_root.isEmpty(); return !m_root.isEmpty();
} }
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
void JsonViewer::printDocument(QPrinter *printer) const void JsonViewer::printDocument(QPrinter *printer) const
{ {
if (!hasContent()) if (!hasContent())
@ -278,7 +283,7 @@ void JsonViewer::printDocument(QPrinter *printer) const
doc.print(printer); doc.print(printer);
} }
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
QByteArray JsonViewer::saveState() const QByteArray JsonViewer::saveState() const
{ {

View File

@ -4,7 +4,7 @@
#ifndef JSONVIEWER_H #ifndef JSONVIEWER_H
#define JSONVIEWER_H #define JSONVIEWER_H
#include "abstractviewer.h" #include "viewerinterfaces.h"
#include <QJsonValue> #include <QJsonValue>
#include <QJsonDocument> #include <QJsonDocument>
#include <QAbstractItemModel> #include <QAbstractItemModel>
@ -15,21 +15,25 @@ class QTreeView;
class QListWidget; class QListWidget;
class QListWidgetItem; class QListWidgetItem;
class QLineEdit; class QLineEdit;
class QPrinter;
class JsonViewer : public AbstractViewer class JsonViewer : public ViewerInterface
{ {
Q_GADGET Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0" FILE "jsonviewer.json")
Q_INTERFACES(ViewerInterface)
public: public:
JsonViewer(QFile *file, QWidget *parent, QMainWindow *mainWindow);
~JsonViewer() override; ~JsonViewer() override;
void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
QString viewerName() const override { return staticMetaObject.className(); }; QString viewerName() const override { return staticMetaObject.className(); };
QStringList supportedMimeTypes() const override;
QByteArray saveState() const override; QByteArray saveState() const override;
bool restoreState(QByteArray &) override; bool restoreState(QByteArray &) override;
bool supportsOverview() const override { return true; } bool supportsOverview() const override { return true; }
bool hasContent() const override; bool hasContent() const override;
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
protected: protected:
void printDocument(QPrinter *printer) const override; void printDocument(QPrinter *printer) const override;
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT #endif // QT_ABSTRACTVIEWER_PRINTSUPPORT

View File

@ -0,0 +1 @@
{ "Keys": [ "jsonviewer" ] }

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
../../app/abstractviewer.h ../../app/abstractviewer.cpp
)
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
)
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"
)

View File

@ -25,20 +25,21 @@
#include <QEvent> #include <QEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QScroller> #include <QScroller>
#ifdef QT_ABSTRACTVIEWER_PRINTSUPPORT #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#include <QPrinter> #include <QPrinter>
#include <QPainter> #include <QPainter>
#endif #endif
Q_LOGGING_CATEGORY(lcExample, "qt.examples.pdfviewer") Q_LOGGING_CATEGORY(lcExample, "qt.examples.pdfviewer")
PdfViewer::PdfViewer(QFile *file, QWidget *parent, QMainWindow *mainWindow) : void PdfViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
AbstractViewer(file, new QPdfView(parent), mainWindow), m_document(new QPdfDocument(this))
{ {
AbstractViewer::init(file, new QPdfView(parent), mainWindow);
m_document = new QPdfDocument(this);
m_pdfView = qobject_cast<QPdfView *>(widget()); m_pdfView = qobject_cast<QPdfView *>(widget());
connect(this, &AbstractViewer::uiInitialized, this, &PdfViewer::initPdfViewer); connect(this, &AbstractViewer::uiInitialized, this, &PdfViewer::initPdfViewer);
} }
PdfViewer::~PdfViewer() PdfViewer::~PdfViewer()
{ {
delete m_pages; delete m_pages;
@ -46,6 +47,11 @@ PdfViewer::~PdfViewer()
delete m_document; delete m_document;
} }
QStringList PdfViewer::supportedMimeTypes() const
{
return {"application/pdf"};
}
void PdfViewer::initPdfViewer() void PdfViewer::initPdfViewer()
{ {
m_toolBar = addToolBar("PDF"); m_toolBar = addToolBar("PDF");
@ -154,7 +160,7 @@ bool PdfViewer::hasContent() const
return m_document ? m_document->pageCount() > 0 : false; return m_document ? m_document->pageCount() > 0 : false;
} }
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
void PdfViewer::printDocument(QPrinter *printer) const void PdfViewer::printDocument(QPrinter *printer) const
{ {
if (!hasContent()) if (!hasContent())
@ -172,7 +178,7 @@ void PdfViewer::printDocument(QPrinter *printer) const
} }
painter.end(); painter.end();
} }
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
void PdfViewer::bookmarkSelected(const QModelIndex &index) void PdfViewer::bookmarkSelected(const QModelIndex &index)
{ {

View File

@ -4,7 +4,7 @@
#ifndef PDFVIEWER_H #ifndef PDFVIEWER_H
#define PDFVIEWER_H #define PDFVIEWER_H
#include "abstractviewer.h" #include "viewerinterfaces.h"
#include <QLoggingCategory> #include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(lcExample) Q_DECLARE_LOGGING_CATEGORY(lcExample)
@ -17,22 +17,25 @@ class QListView;
class QTabWidget; class QTabWidget;
class QTreeView; class QTreeView;
class ZoomSelector; class ZoomSelector;
class PdfViewer : public AbstractViewer class PdfViewer : public ViewerInterface
{ {
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "pdfviewer.json")
Q_INTERFACES(ViewerInterface)
public: public:
PdfViewer(QFile *file, QWidget *parent, QMainWindow *mainWindow);
~PdfViewer() override; ~PdfViewer() override;
void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
QString viewerName() const override { return staticMetaObject.className(); }; QString viewerName() const override { return staticMetaObject.className(); };
QStringList supportedMimeTypes() const override;
bool supportsOverview() const override { return true; } bool supportsOverview() const override { return true; }
bool hasContent() const override; bool hasContent() const override;
QByteArray saveState() const override { return QByteArray(); } QByteArray saveState() const override { return QByteArray(); }
bool restoreState(QByteArray &) override { return true; } bool restoreState(QByteArray &) override { return true; }
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
protected: protected:
void printDocument(QPrinter *printer) const override; void printDocument(QPrinter *printer) const override;
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
public slots: public slots:
void openPdfFile(); void openPdfFile();
@ -58,10 +61,10 @@ private:
const qreal zoomMultiplier = qSqrt(2.0); const qreal zoomMultiplier = qSqrt(2.0);
static constexpr int maxIconWidth = 200; static constexpr int maxIconWidth = 200;
QToolBar *m_toolBar = nullptr; QToolBar *m_toolBar = nullptr;
ZoomSelector *m_zoomSelector; ZoomSelector *m_zoomSelector = nullptr;
QPdfPageSelector *m_pageSelector; QPdfPageSelector *m_pageSelector = nullptr;
QPdfDocument *m_document; QPdfDocument *m_document = nullptr;
QPdfView *m_pdfView; QPdfView *m_pdfView = nullptr;
QAction *m_actionForward = nullptr; QAction *m_actionForward = nullptr;
QAction *m_actionBack = nullptr; QAction *m_actionBack = nullptr;
QTreeView *m_bookmarks = nullptr; QTreeView *m_bookmarks = nullptr;

View File

@ -0,0 +1 @@
{ "Keys": [ "pdfviewer" ] }

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
../../app/abstractviewer.h ../../app/abstractviewer.cpp
)
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
)
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"
)

View File

@ -13,14 +13,14 @@
#include <QMetaObject> #include <QMetaObject>
#include <QScrollBar> #include <QScrollBar>
#include <QPainter> #include <QPainter>
#ifdef QT_ABSTRACTVIEWER_PRINTSUPPORT #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
#include <QPrinter> #include <QPrinter>
#include <QPrintDialog> #include <QPrintDialog>
#endif #endif
TxtViewer::TxtViewer(QFile *file, QWidget *parent, QMainWindow *mainWindow) : void TxtViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
AbstractViewer(file, new QPlainTextEdit(parent), mainWindow)
{ {
AbstractViewer::init(file, new QPlainTextEdit(parent), mainWindow);
m_textEdit = qobject_cast<QPlainTextEdit *>(widget()); m_textEdit = qobject_cast<QPlainTextEdit *>(widget());
connect(this, &AbstractViewer::uiInitialized, this, &TxtViewer::setupTxtUi); connect(this, &AbstractViewer::uiInitialized, this, &TxtViewer::setupTxtUi);
} }
@ -29,6 +29,11 @@ TxtViewer::~TxtViewer()
{ {
} }
QStringList TxtViewer::supportedMimeTypes() const
{
return {"text/plain"};
}
void TxtViewer::setupTxtUi() void TxtViewer::setupTxtUi()
{ {
QMenu *editMenu = addMenu(tr("&Edit")); QMenu *editMenu = addMenu(tr("&Edit"));
@ -119,7 +124,7 @@ bool TxtViewer::hasContent() const
return (!m_textEdit->toPlainText().isEmpty()); return (!m_textEdit->toPlainText().isEmpty());
} }
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
void TxtViewer::printDocument(QPrinter *printer) const void TxtViewer::printDocument(QPrinter *printer) const
{ {
if (!hasContent()) if (!hasContent())
@ -127,8 +132,7 @@ void TxtViewer::printDocument(QPrinter *printer) const
m_textEdit->print(printer); m_textEdit->print(printer);
} }
#endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT
bool TxtViewer::saveFile(QFile *file) bool TxtViewer::saveFile(QFile *file)
{ {

View File

@ -4,28 +4,34 @@
#ifndef TXTVIEWER_H #ifndef TXTVIEWER_H
#define TXTVIEWER_H #define TXTVIEWER_H
#include "viewerinterfaces.h"
#include "abstractviewer.h" #include "abstractviewer.h"
#include <QPointer> #include <QPointer>
class QMainWindow; class QMainWindow;
class QPlainTextEdit; class QPlainTextEdit;
class QLabel; class QLabel;
class TxtViewer : public AbstractViewer class TxtViewer : public ViewerInterface
{ {
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface" FILE "txtviewer.json")
Q_INTERFACES(ViewerInterface)
public: public:
TxtViewer(QFile *file, QWidget *parent, QMainWindow *mainWindow);
~TxtViewer() override; ~TxtViewer() override;
void init(QFile *file, QWidget *parent, QMainWindow *mainWindow) override;
QString viewerName() const override { return staticMetaObject.className(); }; QString viewerName() const override { return staticMetaObject.className(); };
QStringList supportedMimeTypes() const override;
bool saveDocument() override { return saveFile(m_file.get()); }; bool saveDocument() override { return saveFile(m_file.get()); };
bool saveDocumentAs() override; bool saveDocumentAs() override;
bool hasContent() const override; bool hasContent() const override;
QByteArray saveState() const override { return QByteArray(); } QByteArray saveState() const override { return QByteArray(); }
bool restoreState(QByteArray &) override { return true; } bool restoreState(QByteArray &) override { return true; }
bool supportsOverview() const override { return false; }
#if defined(QT_ABSTRACTVIEWER_PRINTSUPPORT) #ifdef QT_DOCUMENTVIEWER_PRINTSUPPORT
protected: protected:
void printDocument(QPrinter *printer) const override; void printDocument(QPrinter *printer) const override;
#endif // QT_ABSTRACTVIEWER_PRINTSUPPORT #endif // QT_DOCUMENTVIEWER_PRINTSUPPORT
private slots: private slots:
void setupTxtUi(); void setupTxtUi();

View File

@ -0,0 +1 @@
{ "Keys": [ "txtviewer" ] }

View File

@ -1,32 +0,0 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QWidget>
#include <QMimeDatabase>
#include <QMimeType>
#include "viewerfactory.h"
#include "abstractviewer.h"
#include "pdfviewer.h"
#include "txtviewer.h"
#include "jsonviewer.h"
AbstractViewer *ViewerFactory::makeViewer(QFile *file, QWidget *displayWidget,
QMainWindow *mainWindow)
{
Q_ASSERT(file);
const QFileInfo info(*file);
QMimeDatabase db;
const auto mimeType = db.mimeTypeForFile(info);
if (mimeType.inherits("application/json"))
return new JsonViewer(file, displayWidget, mainWindow);
if (mimeType.inherits("text/plain"))
return new TxtViewer(file, displayWidget, mainWindow);
if (mimeType.inherits("application/pdf"))
return new PdfViewer(file, displayWidget, mainWindow);
// Default to text viewer
return new TxtViewer(file, displayWidget, mainWindow);
}

View File

@ -1,19 +0,0 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef VIEWERFACTORY_H
#define VIEWERFACTORY_H
class AbstractViewer;
class QWidget;
class QMainWindow;
class Questions;
class QFile;
class ViewerFactory
{
public:
ViewerFactory() = delete;
static AbstractViewer *makeViewer(QFile *file, QWidget *displayWidget, QMainWindow *mainWindow);
};
#endif // VIEWERFACTORY_H

View File

@ -14,7 +14,7 @@ set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/demos/photosurface")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools) find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick ShaderTools)
qt_standard_project_setup(REQUIRES 6.5) qt_standard_project_setup()
if (WIN32) if (WIN32)
#! [appicon_windows] #! [appicon_windows]
@ -48,6 +48,7 @@ target_link_libraries(photosurfaceexample PRIVATE
qt_add_qml_module(photosurfaceexample qt_add_qml_module(photosurfaceexample
URI photosurface URI photosurface
VERSION 1.0
QML_FILES QML_FILES
"photosurface.qml" "photosurface.qml"
"resources/MomentumAnimation.qml" "resources/MomentumAnimation.qml"