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
+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" ] }