Issue #118: Continuation

This commit is contained in:
Jakub Melka
2023-12-04 20:04:40 +01:00
parent bc8be2198b
commit edd100d3b8
78 changed files with 873 additions and 823 deletions

View File

@@ -47,13 +47,19 @@ add_library(Pdf4QtLibWidgets SHARED
sources/pdfselectpagesdialog.h
sources/pdfselectpagesdialog.cpp
sources/pdfselectpagesdialog.ui
sources/pdfwidgetannotation.h
sources/pdfwidgetannotation.cpp
sources/pdfpagecontentelements.cpp
sources/pdfpagecontentelements.h
sources/pdfitemmodels.cpp
sources/pdfitemmodels.h
)
include(GenerateExportHeader)
GENERATE_EXPORT_HEADER(Pdf4QtLibWidgets
EXPORT_MACRO_NAME
PDF4QTLIBSHARED_EXPORT
PDF4QTLIBCORESHARED_EXPORT
EXPORT_FILE_NAME "${CMAKE_BINARY_DIR}/${INSTALL_INCLUDEDIR}/pdf4qtlibwidgets_export.h")
if(PDF4QT_ENABLE_OPENGL)

View File

@@ -29,7 +29,7 @@ namespace pdf
/// Tool that creates 'sticky note' annotations. Multiple types of sticky
/// notes are available, user can select a type of sticky note. When
/// user select a point, popup window appears and user can enter a text.
class PDF4QTLIBSHARED_EXPORT PDFCreateStickyNoteTool : public PDFWidgetTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateStickyNoteTool : public PDFWidgetTool
{
Q_OBJECT
@@ -52,7 +52,7 @@ private:
TextAnnotationIcon m_icon;
};
class PDF4QTLIBSHARED_EXPORT PDFCreateAnnotationTool : public PDFWidgetTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateAnnotationTool : public PDFWidgetTool
{
Q_OBJECT
@@ -69,7 +69,7 @@ protected:
/// Tool that creates url link annotation. Multiple types of link highlights
/// are available, user can select a link highlight. When link annotation
/// is clicked, url address is triggered.
class PDF4QTLIBSHARED_EXPORT PDFCreateHyperlinkTool : public PDFCreateAnnotationTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateHyperlinkTool : public PDFCreateAnnotationTool
{
Q_OBJECT
@@ -91,7 +91,7 @@ private:
};
/// Tool that creates free text note without callout line.
class PDF4QTLIBSHARED_EXPORT PDFCreateFreeTextTool : public PDFCreateAnnotationTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateFreeTextTool : public PDFCreateAnnotationTool
{
Q_OBJECT
@@ -109,7 +109,7 @@ private:
};
/// Tool that creates line/polyline/polygon annotations.
class PDF4QTLIBSHARED_EXPORT PDFCreateLineTypeTool : public PDFCreateAnnotationTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateLineTypeTool : public PDFCreateAnnotationTool
{
Q_OBJECT
@@ -160,7 +160,7 @@ private:
};
/// Tool that creates ellipse annotation.
class PDF4QTLIBSHARED_EXPORT PDFCreateEllipseTool : public PDFCreateAnnotationTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateEllipseTool : public PDFCreateAnnotationTool
{
Q_OBJECT
@@ -196,7 +196,7 @@ private:
QColor m_fillColor;
};
class PDF4QTLIBSHARED_EXPORT PDFCreateFreehandCurveTool : public PDFCreateAnnotationTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateFreehandCurveTool : public PDFCreateAnnotationTool
{
Q_OBJECT
@@ -234,7 +234,7 @@ private:
/// Tool that creates 'stamp' annotations. Multiple types of stamps
/// are available, user can select a type of stamp (text).
class PDF4QTLIBSHARED_EXPORT PDFCreateStampTool : public PDFWidgetTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateStampTool : public PDFWidgetTool
{
Q_OBJECT
@@ -267,7 +267,7 @@ private:
};
/// Tool for highlighting of text in document
class PDF4QTLIBSHARED_EXPORT PDFCreateHighlightTextTool : public PDFWidgetTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateHighlightTextTool : public PDFWidgetTool
{
Q_OBJECT
@@ -319,7 +319,7 @@ private:
/// Tool that creates redaction annotation from rectangle. Rectangle is not
/// selected from the text, it is just any rectangle.
class PDF4QTLIBSHARED_EXPORT PDFCreateRedactRectangleTool : public PDFCreateAnnotationTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateRedactRectangleTool : public PDFCreateAnnotationTool
{
Q_OBJECT
@@ -337,7 +337,7 @@ private:
};
/// Tool for redaction of text in document. Creates redaction annotation from text selection.
class PDF4QTLIBSHARED_EXPORT PDFCreateRedactTextTool : public PDFWidgetTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreateRedactTextTool : public PDFWidgetTool
{
Q_OBJECT

View File

@@ -33,7 +33,7 @@ class PDFCertificateManagerDialog;
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFCertificateManagerDialog : public QDialog
class PDF4QTLIBCORESHARED_EXPORT PDFCertificateManagerDialog : public QDialog
{
Q_OBJECT

View File

@@ -30,7 +30,7 @@ class PDFCreateCertificateDialog;
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFCreateCertificateDialog : public QDialog
class PDF4QTLIBCORESHARED_EXPORT PDFCreateCertificateDialog : public QDialog
{
Q_OBJECT

View File

@@ -54,7 +54,7 @@ public:
virtual bool doEvent(QEvent* event) = 0;
};
class PDF4QTLIBSHARED_EXPORT PDFWidget : public QWidget
class PDF4QTLIBCORESHARED_EXPORT PDFWidget : public QWidget
{
Q_OBJECT

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,332 @@
// Copyright (C) 2019-2021 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFITEMMODELS_H
#define PDFITEMMODELS_H
#include "pdfglobal.h"
#include "pdfobject.h"
#include <QIcon>
#include <QPixmapCache>
#include <QAbstractItemModel>
#include <set>
namespace pdf
{
class PDFAction;
class PDFDocument;
class PDFOutlineItem;
class PDFModifiedDocument;
class PDFFileSpecification;
class PDFOptionalContentActivity;
class PDFDrawWidgetProxy;
class PDFDestination;
/// Represents tree item in the GUI tree
class PDF4QTLIBCORESHARED_EXPORT PDFTreeItem
{
public:
inline explicit PDFTreeItem() = default;
inline explicit PDFTreeItem(PDFTreeItem* parent) : m_parent(parent) { }
virtual ~PDFTreeItem();
template<typename T, typename... Arguments>
inline T* addChild(Arguments&&... arguments)
{
T* item = new T(this, std::forward(arguments)...);
m_children.push_back(item);
return item;
}
template<typename T, typename... Arguments>
inline T* insertChild(int position, Arguments&&... arguments)
{
T* item = new T(this, std::forward(arguments)...);
m_children.insert(std::next(m_children.begin(), position), item);
return item;
}
void insertCreatedChild(int position, PDFTreeItem* item)
{
item->m_parent = this;
m_children.insert(std::next(m_children.begin(), position), item);
}
void addCreatedChild(PDFTreeItem* item)
{
item->m_parent = this;
m_children.push_back(item);
}
int getRow() const { return m_parent->m_children.indexOf(const_cast<PDFTreeItem*>(this)); }
int getChildCount() const { return m_children.size(); }
const PDFTreeItem* getChild(int index) const { return m_children.at(index); }
PDFTreeItem* getChild(int index) { return m_children.at(index); }
const PDFTreeItem* getParent() const { return m_parent; }
PDFTreeItem* takeChild(int index);
private:
PDFTreeItem* m_parent = nullptr;
QList<PDFTreeItem*> m_children;
};
/// Root of all tree item models. Reimplementations of this model
/// must handle "soft" document updates, such as only annotations changed etc.
/// Model should be rebuilded only, if it is neccessary.
class PDF4QTLIBCORESHARED_EXPORT PDFTreeItemModel : public QAbstractItemModel
{
public:
explicit PDFTreeItemModel(QObject* parent);
void setDocument(const PDFModifiedDocument& document);
bool isEmpty() const;
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& child) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual bool hasChildren(const QModelIndex& parent) const override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual void update() = 0;
protected:
const PDFDocument* m_document;
std::unique_ptr<PDFTreeItem> m_rootItem;
};
class PDFOptionalContentTreeItem : public PDFTreeItem
{
public:
inline explicit PDFOptionalContentTreeItem(PDFOptionalContentTreeItem* parent, PDFObjectReference reference, QString text, bool locked) :
PDFTreeItem(parent),
m_reference(reference),
m_text(qMove(text)),
m_locked(locked)
{
}
PDFObjectReference getReference() const { return m_reference; }
QString getText() const;
bool isLocked() const { return m_locked; }
private:
PDFObjectReference m_reference; ///< Reference to optional content group
QString m_text; ///< Node display name
bool m_locked; ///< Node is locked (user can't change it)
};
class PDF4QTLIBCORESHARED_EXPORT PDFOptionalContentTreeItemModel : public PDFTreeItemModel
{
Q_OBJECT
public:
inline explicit PDFOptionalContentTreeItemModel(QObject* parent) :
PDFTreeItemModel(parent),
m_activity(nullptr)
{
}
void setActivity(PDFOptionalContentActivity* activity);
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual void update() override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override;
private:
PDFOptionalContentActivity* m_activity;
};
class PDFOutlineTreeItem : public PDFTreeItem
{
public:
explicit PDFOutlineTreeItem(PDFOutlineTreeItem* parent, QSharedPointer<PDFOutlineItem> outlineItem);
const PDFOutlineItem* getOutlineItem() const { return m_outlineItem.data(); }
PDFOutlineItem* getOutlineItem() { return m_outlineItem.data(); }
private:
QSharedPointer<PDFOutlineItem> m_outlineItem;
};
class PDF4QTLIBCORESHARED_EXPORT PDFOutlineTreeItemModel : public PDFTreeItemModel
{
Q_OBJECT
public:
PDFOutlineTreeItemModel(QIcon icon, bool editable, QObject* parent) :
PDFTreeItemModel(parent),
m_icon(qMove(icon)),
m_editable(editable)
{
}
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual void update() override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override;
virtual Qt::DropActions supportedDropActions() const override;
virtual Qt::DropActions supportedDragActions() const override;
virtual bool insertRows(int row, int count, const QModelIndex& parent) override;
virtual bool removeRows(int row, int count, const QModelIndex& parent) override;
virtual bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count, const QModelIndex& destinationParent, int destinationChild) override;
virtual QStringList mimeTypes() const override;
virtual QMimeData* mimeData(const QModelIndexList& indexes) const override;
virtual bool canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const override;
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override;
/// Returns action assigned to the index. If index is invalid, or
/// points to the invalid item, nullptr is returned.
/// \param index Index of the outline item
const PDFAction* getAction(const QModelIndex& index) const;
/// Returns the outline item for the given index, or nullptr
/// if no outline item is assigned to that index.
const PDFOutlineItem* getOutlineItem(const QModelIndex& index) const;
/// Returns the outline item for the given index, or nullptr
/// if no outline item is assigned to that index.
PDFOutlineItem* getOutlineItem(const QModelIndex& index);
void setFontBold(const QModelIndex& index, bool value);
void setFontItalics(const QModelIndex& index, bool value);
void setDestination(const QModelIndex& index, const PDFDestination& destination);
const PDFOutlineItem* getRootOutlineItem() const;
bool isEditable() const { return m_editable; }
private:
QIcon m_icon;
bool m_editable;
mutable QSharedPointer<PDFOutlineItem> m_dragDropItem;
};
class PDF4QTLIBCORESHARED_EXPORT PDFSelectableOutlineTreeItemModel : public PDFOutlineTreeItemModel
{
Q_OBJECT
private:
using BaseClass = PDFOutlineTreeItemModel;
public:
PDFSelectableOutlineTreeItemModel(QIcon icon, QObject* parent) :
BaseClass(qMove(icon), false, parent)
{
}
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual void update() override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role) override;
std::vector<const PDFOutlineItem*> getSelectedItems() const;
private:
std::set<const PDFOutlineItem*> m_selectedItems;
};
class PDFAttachmentsTreeItem : public PDFTreeItem
{
public:
explicit PDFAttachmentsTreeItem(PDFAttachmentsTreeItem* parent, QIcon icon, QString title, QString description, const PDFFileSpecification* fileSpecification);
virtual ~PDFAttachmentsTreeItem() override;
const QIcon& getIcon() const { return m_icon; }
const QString& getTitle() const { return m_title; }
const QString& getDescription() const { return m_description; }
const PDFFileSpecification* getFileSpecification() const { return m_fileSpecification.get(); }
private:
QIcon m_icon;
QString m_title;
QString m_description;
std::unique_ptr<PDFFileSpecification> m_fileSpecification;
};
class PDF4QTLIBCORESHARED_EXPORT PDFAttachmentsTreeItemModel : public PDFTreeItemModel
{
Q_OBJECT
public:
PDFAttachmentsTreeItemModel(QObject* parent) :
PDFTreeItemModel(parent)
{
}
enum Column
{
Title,
Description,
EndColumn
};
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
virtual void update() override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
const PDFFileSpecification* getFileSpecification(const QModelIndex& index) const;
};
class PDF4QTLIBCORESHARED_EXPORT PDFThumbnailsItemModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit PDFThumbnailsItemModel(const PDFDrawWidgetProxy* proxy, QObject* parent);
bool isEmpty() const;
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
virtual QModelIndex parent(const QModelIndex& child) const override;
virtual int rowCount(const QModelIndex& parent) const override;
virtual int columnCount(const QModelIndex& parent) const override;
virtual QVariant data(const QModelIndex& index, int role) const override;
void setThumbnailsSize(int size);
void setDocument(const PDFModifiedDocument& document);
/// Sets the extra item width/height for size hint. This space will be added to the size hint (pixmap size)
void setExtraItemSizeHint(int width, int height) { m_extraItemWidthHint = width; m_extraItemHeighHint = height; }
PDFInteger getPageIndex(const QModelIndex& index) const;
private:
void onPageImageChanged(bool all, const std::vector<PDFInteger>& pages);
/// Returns generated key for page index
QString getKey(int pageIndex) const;
const PDFDrawWidgetProxy* m_proxy;
int m_thumbnailSize;
int m_extraItemWidthHint;
int m_extraItemHeighHint;
int m_pageCount;
const PDFDocument* m_document;
QPixmapCache m_thumbnailCache;
};
} // namespace pdf
#endif // PDFITEMMODELS_H

View File

@@ -38,7 +38,7 @@ namespace pdf
{
class PDFPageContentElement;
class PDF4QTLIBSHARED_EXPORT PDFPageContentEditorStyleSettings : public QWidget
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentEditorStyleSettings : public QWidget
{
Q_OBJECT

View File

@@ -33,7 +33,7 @@ class PDFPageContentElementRectangle;
class PDFPageContentElementFreehandCurve;
class PDFTextEditPseudowidget;
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementTool : public PDFWidgetTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementTool : public PDFWidgetTool
{
Q_OBJECT
public:
@@ -58,7 +58,7 @@ protected:
};
/// Tool that creates rectangle element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementRectangleTool : public PDFCreatePCElementTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementRectangleTool : public PDFCreatePCElementTool
{
Q_OBJECT
@@ -91,7 +91,7 @@ private:
};
/// Tool that displays SVG image (or raster image)
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementImageTool : public PDFCreatePCElementTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementImageTool : public PDFCreatePCElementTool
{
Q_OBJECT
@@ -131,7 +131,7 @@ private:
};
/// Tool that creates line element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementLineTool : public PDFCreatePCElementTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementLineTool : public PDFCreatePCElementTool
{
Q_OBJECT
@@ -167,7 +167,7 @@ private:
};
/// Tool that creates dot element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementDotTool : public PDFCreatePCElementTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementDotTool : public PDFCreatePCElementTool
{
Q_OBJECT
@@ -199,7 +199,7 @@ private:
};
/// Tool that creates freehand curve element.
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementFreehandCurveTool : public PDFCreatePCElementTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementFreehandCurveTool : public PDFCreatePCElementTool
{
Q_OBJECT
@@ -237,7 +237,7 @@ private:
};
/// Tool that displays SVG image
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementTextTool : public PDFCreatePCElementTool
class PDF4QTLIBCORESHARED_EXPORT PDFCreatePCElementTextTool : public PDFCreatePCElementTool
{
Q_OBJECT

View File

@@ -38,7 +38,7 @@ class PDFPageContentScene;
class PDFPageContentElement;
class PDFPageContentEditorStyleSettings;
class PDF4QTLIBSHARED_EXPORT PDFPageContentEditorWidget : public QDockWidget
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentEditorWidget : public QDockWidget
{
Q_OBJECT

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,626 @@
// Copyright (C) 2022 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#ifndef PDFPAGECONTENTELEMENTS_H
#define PDFPAGECONTENTELEMENTS_H
#include "pdfdocumentdrawinterface.h"
#include <QPen>
#include <QFont>
#include <QBrush>
#include <QCursor>
#include <QPainterPath>
#include <set>
class QSvgRenderer;
namespace pdf
{
class PDFWidget;
class PDFDocument;
class PDFPageContentScene;
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElement
{
public:
explicit PDFPageContentElement() = default;
virtual ~PDFPageContentElement() = default;
virtual PDFPageContentElement* clone() const = 0;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const = 0;
/// Returns manipulation mode. If manipulation mode is zero, then element
/// cannot be manipulated. If it is nonzero, then element can be manipulated
/// in some way.
/// \param point Point on page
/// \param snapPointDistanceTreshold Snap point threshold
virtual uint getManipulationMode(const QPointF& point, PDFReal snapPointDistanceThreshold) const = 0;
/// Performs manipulation of given mode. Mode must be the one returned
/// from function getManipulationMode. \sa getManipulationMode
/// \param mode Mode
/// \param offset Offset
virtual void performManipulation(uint mode, const QPointF& offset) = 0;
/// Returns bounding box of the element
virtual QRectF getBoundingBox() const = 0;
/// Sets size to the elements that supports it. Does
/// nothing for elements, which does not support it.
virtual void setSize(QSizeF size) = 0;
/// Returns description of the element
virtual QString getDescription() const = 0;
PDFInteger getPageIndex() const;
void setPageIndex(PDFInteger newPageIndex);
PDFInteger getElementId() const;
void setElementId(PDFInteger newElementId);
/// Returns cursor shape for manipulation mode
/// \param mode Manipulation mode
static Qt::CursorShape getCursorShapeForManipulationMode(uint mode);
enum ManipulationModes : uint
{
None = 0,
Translate,
Top,
Left,
Right,
Bottom,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
Pt1,
Pt2
};
protected:
uint getRectangleManipulationMode(const QRectF& rectangle,
const QPointF& point,
PDFReal snapPointDistanceThreshold) const;
void performRectangleManipulation(QRectF& rectangle,
uint mode,
const QPointF& offset);
void performRectangleSetSize(QRectF& rectangle, QSizeF size);
QString formatDescription(const QString& description) const;
PDFInteger m_elementId = -1;
PDFInteger m_pageIndex = -1;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentStyledElement : public PDFPageContentElement
{
public:
explicit PDFPageContentStyledElement() = default;
virtual ~PDFPageContentStyledElement() = default;
const QPen& getPen() const;
void setPen(const QPen& newPen);
const QBrush& getBrush() const;
void setBrush(const QBrush& newBrush);
protected:
QPen m_pen;
QBrush m_brush;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElementRectangle : public PDFPageContentStyledElement
{
public:
virtual ~PDFPageContentElementRectangle() = default;
virtual PDFPageContentElementRectangle* clone() const override;
bool isRounded() const;
void setRounded(bool newRounded);
const QRectF& getRectangle() const;
void setRectangle(const QRectF& newRectangle);
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual uint getManipulationMode(const QPointF& point,
PDFReal snapPointDistanceThreshold) const override;
virtual void performManipulation(uint mode, const QPointF& offset) override;
virtual QRectF getBoundingBox() const override;
virtual void setSize(QSizeF size) override;
virtual QString getDescription() const override;
private:
bool m_rounded = false;
QRectF m_rectangle;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElementLine : public PDFPageContentStyledElement
{
public:
virtual ~PDFPageContentElementLine() = default;
virtual PDFPageContentElementLine* clone() const override;
enum class LineGeometry
{
General,
Horizontal,
Vertical
};
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual uint getManipulationMode(const QPointF& point,
PDFReal snapPointDistanceThreshold) const override;
virtual void performManipulation(uint mode, const QPointF& offset) override;
virtual QRectF getBoundingBox() const override;
virtual void setSize(QSizeF size) override;
virtual QString getDescription() const override;
LineGeometry getGeometry() const;
void setGeometry(LineGeometry newGeometry);
const QLineF& getLine() const;
void setLine(const QLineF& newLine);
private:
LineGeometry m_geometry = LineGeometry::General;
QLineF m_line;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElementDot : public PDFPageContentStyledElement
{
public:
virtual ~PDFPageContentElementDot() = default;
virtual PDFPageContentElementDot* clone() const override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual uint getManipulationMode(const QPointF& point,
PDFReal snapPointDistanceThreshold) const override;
virtual void performManipulation(uint mode, const QPointF& offset) override;
virtual QRectF getBoundingBox() const override;
virtual void setSize(QSizeF size) override;
virtual QString getDescription() const override;
QPointF getPoint() const;
void setPoint(QPointF newPoint);
private:
QPointF m_point;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElementFreehandCurve : public PDFPageContentStyledElement
{
public:
virtual ~PDFPageContentElementFreehandCurve() = default;
virtual PDFPageContentElementFreehandCurve* clone() const override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual uint getManipulationMode(const QPointF& point,
PDFReal snapPointDistanceThreshold) const override;
virtual void performManipulation(uint mode, const QPointF& offset) override;
virtual QRectF getBoundingBox() const override;
virtual void setSize(QSizeF size);
virtual QString getDescription() const override;
QPainterPath getCurve() const;
void setCurve(QPainterPath newCurve);
bool isEmpty() const { return m_curve.isEmpty(); }
void addStartPoint(const QPointF& point);
void addPoint(const QPointF& point);
void clear();
private:
QPainterPath m_curve;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentImageElement : public PDFPageContentElement
{
public:
PDFPageContentImageElement();
virtual ~PDFPageContentImageElement();
virtual PDFPageContentImageElement* clone() const override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual uint getManipulationMode(const QPointF& point,
PDFReal snapPointDistanceThreshold) const override;
virtual void performManipulation(uint mode, const QPointF& offset) override;
virtual QRectF getBoundingBox() const override;
virtual void setSize(QSizeF size);
virtual QString getDescription() const override;
const QByteArray& getContent() const;
void setContent(const QByteArray& newContent);
const QRectF& getRectangle() const;
void setRectangle(const QRectF& newRectangle);
private:
QRectF m_rectangle;
QByteArray m_content;
QImage m_image;
std::unique_ptr<QSvgRenderer> m_renderer;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElementTextBox : public PDFPageContentStyledElement
{
public:
virtual ~PDFPageContentElementTextBox() = default;
virtual PDFPageContentElementTextBox* clone() const override;
const QRectF& getRectangle() const { return m_rectangle; }
void setRectangle(const QRectF& newRectangle) { m_rectangle = newRectangle; }
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
virtual uint getManipulationMode(const QPointF& point,
PDFReal snapPointDistanceThreshold) const override;
virtual void performManipulation(uint mode, const QPointF& offset) override;
virtual QRectF getBoundingBox() const override;
virtual void setSize(QSizeF size) override;
virtual QString getDescription() const override;
const QString& getText() const;
void setText(const QString& newText);
const QFont& getFont() const;
void setFont(const QFont& newFont);
PDFReal getAngle() const;
void setAngle(PDFReal newAngle);
const Qt::Alignment& getAlignment() const;
void setAlignment(const Qt::Alignment& newAlignment);
private:
QString m_text;
QRectF m_rectangle;
QFont m_font;
PDFReal m_angle = 0.0;
Qt::Alignment m_alignment = Qt::AlignCenter;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentElementManipulator : public QObject
{
Q_OBJECT
public:
PDFPageContentElementManipulator(PDFPageContentScene* scene, QObject* parent);
enum class Operation
{
AlignTop,
AlignCenterVertically,
AlignBottom,
AlignLeft,
AlignCenterHorizontally,
AlignRight,
SetSameHeight,
SetSameWidth,
SetSameSize,
CenterHorizontally,
CenterVertically,
CenterHorAndVert,
LayoutVertically,
LayoutHorizontally,
LayoutForm,
LayoutGrid
};
enum SelectionMode
{
NoUpdate = 0x0000,
Clear = 0x0001, ///< Clears current selection
Select = 0x0002, ///< Selects item
Deselect = 0x0004, ///< Deselects item
Toggle = 0x0008, ///< Toggles selection of the item
};
Q_DECLARE_FLAGS(SelectionModes, SelectionMode)
/// Returns true, if element with given id is selected
/// \param id Element id
bool isSelected(PDFInteger id) const;
/// Returns true, if all elements are selected
/// \param ids Element ids
bool isAllSelected(const std::set<PDFInteger>& elementIds) const;
/// Returns true, if selection is empty
bool isSelectionEmpty() const { return m_selection.empty(); }
/// Clear all selection, stop manipulation
void reset();
void update(PDFInteger id, SelectionModes modes);
void update(const std::set<PDFInteger>& ids, SelectionModes modes);
void select(PDFInteger id);
void select(const std::set<PDFInteger>& ids);
void selectNew(PDFInteger id);
void selectNew(const std::set<PDFInteger>& ids);
void deselect(PDFInteger id);
void deselect(const std::set<PDFInteger>& ids);
void selectAll();
void deselectAll();
bool isManipulationAllowed(PDFInteger pageIndex) const;
bool isManipulationInProgress() const { return m_isManipulationInProgress; }
void performOperation(Operation operation);
void performDeleteSelection();
void startManipulation(PDFInteger pageIndex,
const QPointF& startPoint,
const QPointF& currentPoint,
PDFReal snapPointDistanceThreshold);
void updateManipulation(PDFInteger pageIndex,
const QPointF& startPoint,
const QPointF& currentPoint);
void finishManipulation(PDFInteger pageIndex,
const QPointF& startPoint,
const QPointF& currentPoint,
bool createCopy);
void cancelManipulation();
void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const;
/// Returns bounding box of whole selection
QRectF getSelectionBoundingRect() const;
/// Returns page rectangle for the page
QRectF getPageMediaBox(PDFInteger pageIndex) const;
signals:
void selectionChanged();
void stateChanged();
private:
void eraseSelectedElementById(PDFInteger id);
PDFPageContentScene* m_scene;
std::vector<PDFInteger> m_selection;
bool m_isManipulationInProgress;
std::vector<std::unique_ptr<PDFPageContentElement>> m_manipulatedElements;
std::map<PDFInteger, uint> m_manipulationModes;
QPointF m_lastUpdatedPoint;
};
class PDF4QTLIBCORESHARED_EXPORT PDFPageContentScene : public QObject,
public IDocumentDrawInterface,
public IDrawWidgetInputInterface
{
Q_OBJECT
public:
explicit PDFPageContentScene(QObject* parent);
virtual ~PDFPageContentScene();
static constexpr PDFInteger INVALID_ELEMENT_ID = 0;
/// Add new element to page content scene, scene
/// takes ownership over the element.
/// \param element Element
void addElement(PDFPageContentElement* element);
/// Replaces element in the page content scene, scene
/// takes ownership over the element.
/// \param element Element
void replaceElement(PDFPageContentElement* element);
/// Returns element by its id (identifier number)
/// \param id Element id
PDFPageContentElement* getElementById(PDFInteger id) const;
/// Clear whole scene - remove all page content elements
void clear();
/// Returns true, if scene is empty
bool isEmpty() const { return m_elements.empty(); }
bool isActive() const;
void setActive(bool newIsActive);
/// Returns set of all element ids
std::set<PDFInteger> getElementIds() const;
/// Returns set of selected element ids
std::set<PDFInteger> getSelectedElementIds() const;
/// Returns set of involved pages
std::set<PDFInteger> getPageIndices() const;
/// Returns bounding box of elements on page
QRectF getBoundingBox(PDFInteger pageIndex) const;
/// Set selected items
void setSelectedElementIds(const std::set<PDFInteger>& selectedElementIds);
/// Removes elements specified in selection
/// \param selection Items to be removed
void removeElementsById(const std::vector<PDFInteger>& selection);
/// Performs manipulation of selected elements with manipulator
void performOperation(int operation);
/// Returns document (or nullptr)
const PDFDocument* getDocument() const;
// IDrawWidgetInputInterface interface
public:
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
virtual QString getTooltip() const override;
virtual const std::optional<QCursor>& getCursor() const override;
virtual int getInputPriority() const override;
virtual void drawPage(QPainter* painter,
PDFInteger pageIndex,
const PDFPrecompiledPage* compiledPage,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
QList<PDFRenderError>& errors) const override;
PDFWidget* widget() const;
void setWidget(PDFWidget* newWidget);
void drawElements(QPainter* painter,
PDFInteger pageIndex,
PDFTextLayoutGetter& layoutGetter,
const QTransform& pagePointToDevicePointMatrix,
const PDFPrecompiledPage* compiledPage,
QList<PDFRenderError>& errors) const;
signals:
/// This signal is emitted when scene has changed (including graphics)
void sceneChanged(bool graphicsOnly);
void selectionChanged();
/// Request to edit the elements
void editElementRequest(const std::set<PDFInteger>& elements);
private:
struct MouseEventInfo
{
std::set<PDFInteger> hoveredElementIds;
QPoint widgetMouseStartPos;
QPoint widgetMouseCurrentPos;
QElapsedTimer timer;
PDFInteger pageIndex = -1;
QPointF pagePos;
bool isValid() const { return !hoveredElementIds.empty(); }
};
MouseEventInfo getMouseEventInfo(QWidget* widget, QPoint point);
struct MouseGrabInfo
{
MouseEventInfo info;
int mouseGrabNesting = 0;
bool isMouseGrabbed() const { return mouseGrabNesting > 0; }
};
PDFReal getSnapPointDistanceThreshold() const;
bool isMouseGrabbed() const { return m_mouseGrabInfo.isMouseGrabbed(); }
/// Grabs mouse input, if mouse is already grabbed, or if event
/// is accepted.
/// \param info Mouse event info
/// \param event Mouse event
void grabMouse(const MouseEventInfo& info, QMouseEvent* event);
/// Release mouse input
/// \param info Mouse event info
/// \param event Mouse event
void ungrabMouse(const MouseEventInfo& info, QMouseEvent* event);
/// Updates mouse cursor
/// \param info Mouse event info
/// \param snapPointDistanceTreshold Snap point threshold
void updateMouseCursor(const MouseEventInfo& info, PDFReal snapPointDistanceThreshold);
/// Reaction on selection changed
void onSelectionChanged();
PDFInteger m_firstFreeId;
bool m_isActive;
PDFWidget* m_widget;
std::vector<std::unique_ptr<PDFPageContentElement>> m_elements;
std::optional<QCursor> m_cursor;
PDFPageContentElementManipulator m_manipulator;
MouseGrabInfo m_mouseGrabInfo;
};
} // namespace pdf
Q_DECLARE_OPERATORS_FOR_FLAGS(pdf::PDFPageContentElementManipulator::SelectionModes)
#endif // PDFPAGECONTENTELEMENTS_H

View File

@@ -31,7 +31,7 @@ namespace pdf
{
class PDFWidget;
class PDF4QTLIBSHARED_EXPORT PDFRenderingErrorsWidget : public QDialog
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFRenderingErrorsWidget : public QDialog
{
Q_OBJECT

View File

@@ -30,7 +30,7 @@ class PDFSelectPagesDialog;
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFSelectPagesDialog : public QDialog
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFSelectPagesDialog : public QDialog
{
Q_OBJECT

View File

@@ -0,0 +1,513 @@
// Copyright (C) 2023 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfdrawwidget.h"
#include "pdfwidgetutils.h"
#include <QMenu>
#include <QDialog>
#include <QApplication>
#include <QMouseEvent>
#include <QGroupBox>
#include <QScrollArea>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QLabel>
#include <QStyleOptionButton>
namespace pdf
{
PDFWidgetAnnotationManager::PDFWidgetAnnotationManager(PDFDrawWidgetProxy* proxy, QObject* parent) :
BaseClass(proxy->getFontCache(), proxy->getCMSManager(), proxy->getOptionalContentActivity(), proxy->getMeshQualitySettings(), proxy->getFeatures(), Target::View, parent),
m_proxy(proxy)
{
Q_ASSERT(proxy);
m_proxy->registerDrawInterface(this);
}
PDFWidgetAnnotationManager::~PDFWidgetAnnotationManager()
{
m_proxy->unregisterDrawInterface(this);
}
void PDFWidgetAnnotationManager::setDocument(const PDFModifiedDocument& document)
{
BaseClass::setDocument(document);
if (document.hasReset() || document.getFlags().testFlag(PDFModifiedDocument::Annotation))
{
m_editableAnnotation = PDFObjectReference();
m_editableAnnotationPage = PDFObjectReference();
}
}
void PDFWidgetAnnotationManager::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFWidgetAnnotationManager::keyPressEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFWidgetAnnotationManager::keyReleaseEvent(QWidget* widget, QKeyEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFWidgetAnnotationManager::mousePressEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
updateFromMouseEvent(event);
// Show context menu?
if (event->button() == Qt::RightButton)
{
PDFWidget* pdfWidget = m_proxy->getWidget();
std::vector<PDFInteger> currentPages = pdfWidget->getDrawWidget()->getCurrentPages();
if (!hasAnyPageAnnotation(currentPages))
{
// All pages doesn't have annotation
return;
}
m_editableAnnotation = PDFObjectReference();
m_editableAnnotationPage = PDFObjectReference();
for (PDFInteger pageIndex : currentPages)
{
PageAnnotations& pageAnnotations = getPageAnnotations(pageIndex);
for (PageAnnotation& pageAnnotation : pageAnnotations.annotations)
{
if (!pageAnnotation.isHovered)
{
continue;
}
if (!PDFAnnotation::isTypeEditable(pageAnnotation.annotation->getType()))
{
continue;
}
m_editableAnnotation = pageAnnotation.annotation->getSelfReference();
m_editableAnnotationPage = pageAnnotation.annotation->getPageReference();
if (!m_editableAnnotationPage.isValid())
{
m_editableAnnotationPage = m_document->getCatalog()->getPage(pageIndex)->getPageReference();
}
break;
}
}
if (m_editableAnnotation.isValid())
{
QMenu menu(tr("Annotation"), pdfWidget);
QAction* showPopupAction = menu.addAction(tr("Show Popup Window"));
QAction* copyAction = menu.addAction(tr("Copy to Multiple Pages"));
QAction* editAction = menu.addAction(tr("Edit"));
QAction* deleteAction = menu.addAction(tr("Delete"));
connect(showPopupAction, &QAction::triggered, this, &PDFWidgetAnnotationManager::onShowPopupAnnotation);
connect(copyAction, &QAction::triggered, this, &PDFWidgetAnnotationManager::onCopyAnnotation);
connect(editAction, &QAction::triggered, this, &PDFWidgetAnnotationManager::onEditAnnotation);
connect(deleteAction, &QAction::triggered, this, &PDFWidgetAnnotationManager::onDeleteAnnotation);
m_editableAnnotationGlobalPosition = pdfWidget->mapToGlobal(event->pos());
menu.exec(m_editableAnnotationGlobalPosition);
}
}
}
void PDFWidgetAnnotationManager::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFWidgetAnnotationManager::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
updateFromMouseEvent(event);
}
void PDFWidgetAnnotationManager::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
{
Q_UNUSED(widget);
updateFromMouseEvent(event);
}
void PDFWidgetAnnotationManager::wheelEvent(QWidget* widget, QWheelEvent* event)
{
Q_UNUSED(widget);
Q_UNUSED(event);
}
void PDFWidgetAnnotationManager::updateFromMouseEvent(QMouseEvent* event)
{
PDFWidget* widget = m_proxy->getWidget();
std::vector<PDFInteger> currentPages = widget->getDrawWidget()->getCurrentPages();
if (!hasAnyPageAnnotation(currentPages))
{
// All pages doesn't have annotation
return;
}
m_tooltip = QString();
m_cursor = std::nullopt;
bool appearanceChanged = false;
// We must update appearance states, and update tooltip
PDFWidgetSnapshot snapshot = m_proxy->getSnapshot();
const bool isDown = event->buttons().testFlag(Qt::LeftButton);
const PDFAppeareanceStreams::Appearance hoverAppearance = isDown ? PDFAppeareanceStreams::Appearance::Down : PDFAppeareanceStreams::Appearance::Rollover;
for (const PDFWidgetSnapshot::SnapshotItem& snapshotItem : snapshot.items)
{
PageAnnotations& pageAnnotations = getPageAnnotations(snapshotItem.pageIndex);
for (PageAnnotation& pageAnnotation : pageAnnotations.annotations)
{
if (pageAnnotation.annotation->isReplyTo())
{
// Annotation is reply to another annotation, do not interact with it
continue;
}
const PDFAppeareanceStreams::Appearance oldAppearance = pageAnnotation.appearance;
QRectF annotationRect = pageAnnotation.annotation->getRectangle();
QTransform matrix = prepareTransformations(snapshotItem.pageToDeviceMatrix, widget, pageAnnotation.annotation->getEffectiveFlags(), m_document->getCatalog()->getPage(snapshotItem.pageIndex), annotationRect);
QPainterPath path;
path.addRect(annotationRect);
path = matrix.map(path);
if (path.contains(event->pos()))
{
pageAnnotation.appearance = hoverAppearance;
pageAnnotation.isHovered = true;
// Generate tooltip
if (m_tooltip.isEmpty())
{
const PDFMarkupAnnotation* markupAnnotation = pageAnnotation.annotation->asMarkupAnnotation();
if (markupAnnotation)
{
QString title = markupAnnotation->getWindowTitle();
if (title.isEmpty())
{
title = markupAnnotation->getSubject();
}
if (title.isEmpty())
{
title = PDFTranslationContext::tr("Info");
}
const size_t repliesCount = pageAnnotations.getReplies(pageAnnotation).size();
if (repliesCount > 0)
{
title = PDFTranslationContext::tr("%1 (%2 replies)").arg(title).arg(repliesCount);
}
m_tooltip = QString("<p><b>%1</b></p><p>%2</p>").arg(title, markupAnnotation->getContents());
}
}
const PDFAction* linkAction = nullptr;
const AnnotationType annotationType = pageAnnotation.annotation->getType();
if (annotationType == AnnotationType::Link)
{
const PDFLinkAnnotation* linkAnnotation = dynamic_cast<const PDFLinkAnnotation*>(pageAnnotation.annotation.data());
Q_ASSERT(linkAnnotation);
// We must check, if user clicked to the link area
QPainterPath activationPath = linkAnnotation->getActivationRegion().getPath();
activationPath = snapshotItem.pageToDeviceMatrix.map(activationPath);
if (activationPath.contains(event->pos()) && linkAnnotation->getAction())
{
m_cursor = QCursor(Qt::PointingHandCursor);
linkAction = linkAnnotation->getAction();
}
}
if (annotationType == AnnotationType::Widget)
{
if (m_formManager && m_formManager->hasFormFieldWidgetText(pageAnnotation.annotation->getSelfReference()))
{
m_cursor = QCursor(Qt::IBeamCursor);
}
else
{
m_cursor = QCursor(Qt::ArrowCursor);
}
}
// Generate popup window
if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton)
{
const PDFMarkupAnnotation* markupAnnotation = pageAnnotation.annotation->asMarkupAnnotation();
if (markupAnnotation)
{
QDialog* dialog = createDialogForMarkupAnnotations(widget, pageAnnotation, pageAnnotations);
// Set proper dialog position - according to the popup annotation. If we
// do not have popup annotation, then try to use annotations rectangle.
if (const PageAnnotation* popupAnnotation = pageAnnotations.getPopupAnnotation(pageAnnotation))
{
QPoint popupPoint = snapshotItem.pageToDeviceMatrix.map(popupAnnotation->annotation->getRectangle().bottomLeft()).toPoint();
popupPoint = widget->mapToGlobal(popupPoint);
dialog->move(popupPoint);
}
else if (markupAnnotation->getRectangle().isValid())
{
QPoint popupPoint = snapshotItem.pageToDeviceMatrix.map(markupAnnotation->getRectangle().bottomRight()).toPoint();
popupPoint = widget->mapToGlobal(popupPoint);
dialog->move(popupPoint);
}
dialog->exec();
}
if (linkAction)
{
Q_EMIT actionTriggered(linkAction);
}
}
}
else
{
pageAnnotation.appearance = PDFAppeareanceStreams::Appearance::Normal;
pageAnnotation.isHovered = false;
}
const bool currentAppearanceChanged = oldAppearance != pageAnnotation.appearance;
if (currentAppearanceChanged)
{
// We have changed appearance - we must mark stream as dirty
pageAnnotation.appearanceStream.dirty();
appearanceChanged = true;
}
}
}
// If appearance has changed, then we must redraw the page
if (appearanceChanged)
{
Q_EMIT widget->getDrawWidgetProxy()->repaintNeeded();
}
}
void PDFWidgetAnnotationManager::onShowPopupAnnotation()
{
PDFWidgetSnapshot snapshot = m_proxy->getSnapshot();
for (const PDFWidgetSnapshot::SnapshotItem& snapshotItem : snapshot.items)
{
PageAnnotations& pageAnnotations = getPageAnnotations(snapshotItem.pageIndex);
for (PageAnnotation& pageAnnotation : pageAnnotations.annotations)
{
if (pageAnnotation.annotation->isReplyTo())
{
// Annotation is reply to another annotation, do not interact with it
continue;
}
if (pageAnnotation.annotation->getSelfReference() == m_editableAnnotation)
{
QDialog* dialog = createDialogForMarkupAnnotations(m_proxy->getWidget(), pageAnnotation, pageAnnotations);
dialog->move(m_editableAnnotationGlobalPosition);
dialog->exec();
return;
}
}
}
}
void PDFWidgetAnnotationManager::onCopyAnnotation()
{
pdf::PDFSelectPagesDialog dialog(tr("Copy Annotation"), tr("Copy Annotation onto Multiple Pages"),
m_document->getCatalog()->getPageCount(), m_proxy->getWidget()->getDrawWidget()->getCurrentPages(), m_proxy->getWidget());
if (dialog.exec() == QDialog::Accepted)
{
std::vector<PDFInteger> pages = dialog.getSelectedPages();
const PDFInteger currentPageIndex = m_document->getCatalog()->getPageIndexFromPageReference(m_editableAnnotationPage);
for (PDFInteger& pageIndex : pages)
{
--pageIndex;
}
auto it = std::find(pages.begin(), pages.end(), currentPageIndex);
if (it != pages.end())
{
pages.erase(it);
}
if (pages.empty())
{
return;
}
PDFDocumentModifier modifier(m_document);
modifier.markAnnotationsChanged();
for (const PDFInteger pageIndex : pages)
{
modifier.getBuilder()->copyAnnotation(m_document->getCatalog()->getPage(pageIndex)->getPageReference(), m_editableAnnotation);
}
if (modifier.finalize())
{
Q_EMIT documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags()));
}
}
}
void PDFWidgetAnnotationManager::onEditAnnotation()
{
PDFEditObjectDialog dialog(EditObjectType::Annotation, m_proxy->getWidget());
PDFObject originalObject = m_document->getObjectByReference(m_editableAnnotation);
dialog.setObject(originalObject);
if (dialog.exec() == PDFEditObjectDialog::Accepted)
{
PDFObject object = dialog.getObject();
if (object != originalObject)
{
PDFDocumentModifier modifier(m_document);
modifier.markAnnotationsChanged();
modifier.getBuilder()->setObject(m_editableAnnotation, object);
modifier.getBuilder()->updateAnnotationAppearanceStreams(m_editableAnnotation);
if (modifier.finalize())
{
Q_EMIT documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags()));
}
}
}
}
void PDFWidgetAnnotationManager::onDeleteAnnotation()
{
if (m_editableAnnotation.isValid())
{
PDFDocumentModifier modifier(m_document);
modifier.markAnnotationsChanged();
modifier.getBuilder()->removeAnnotation(m_editableAnnotationPage, m_editableAnnotation);
if (modifier.finalize())
{
Q_EMIT documentModified(PDFModifiedDocument(modifier.getDocument(), nullptr, modifier.getFlags()));
}
}
}
QDialog* PDFWidgetAnnotationManager::createDialogForMarkupAnnotations(PDFWidget* widget,
const PageAnnotation& pageAnnotation,
const PageAnnotations& pageAnnotations)
{
QDialog* dialog = new QDialog(widget->getDrawWidget()->getWidget(), Qt::Popup);
dialog->setAttribute(Qt::WA_DeleteOnClose, true);
createWidgetsForMarkupAnnotations(dialog, pageAnnotation, pageAnnotations);
return dialog;
}
void PDFWidgetAnnotationManager::createWidgetsForMarkupAnnotations(QWidget* parentWidget,
const PageAnnotation& pageAnnotation,
const PageAnnotations& pageAnnotations)
{
std::vector<const PageAnnotation*> replies = pageAnnotations.getReplies(pageAnnotation);
replies.insert(replies.begin(), &pageAnnotation);
QScrollArea* scrollArea = new QScrollArea(parentWidget);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QVBoxLayout* layout = new QVBoxLayout(parentWidget);
layout->addWidget(scrollArea);
layout->setContentsMargins(QMargins());
QWidget* frameWidget = new QWidget(scrollArea);
QVBoxLayout* frameLayout = new QVBoxLayout(frameWidget);
frameLayout->setSpacing(0);
scrollArea->setWidget(frameWidget);
const PDFMarkupAnnotation* markupMainAnnotation = pageAnnotation.annotation->asMarkupAnnotation();
QColor color = markupMainAnnotation->getDrawColorFromAnnotationColor(markupMainAnnotation->getColor(), 1.0);
QColor titleColor = QColor::fromHslF(color.hueF(), color.saturationF(), 0.2f, 1.0f);
QColor backgroundColor = QColor::fromHslF(color.hueF(), color.saturationF(), 0.9f, 1.0f);
QString style = "QGroupBox { "
"border: 2px solid black; "
"border-color: rgb(%4, %5, %6); "
"margin-top: 3ex; "
"background-color: rgb(%1, %2, %3); "
"}"
"QGroupBox::title { "
"subcontrol-origin: margin; "
"subcontrol-position: top center; "
"padding: 0px 8192px; "
"background-color: rgb(%4, %5, %6); "
"color: #FFFFFF;"
"}";
style = style.arg(backgroundColor.red()).arg(backgroundColor.green()).arg(backgroundColor.blue()).arg(titleColor.red()).arg(titleColor.green()).arg(titleColor.blue());
for (const PageAnnotation* annotation : replies)
{
const PDFMarkupAnnotation* markupAnnotation = annotation->annotation->asMarkupAnnotation();
if (!markupAnnotation)
{
// This should not happen...
continue;
}
QGroupBox* groupBox = new QGroupBox(scrollArea);
frameLayout->addWidget(groupBox);
QString title = markupAnnotation->getWindowTitle();
if (title.isEmpty())
{
title = markupAnnotation->getSubject();
}
QString dateTimeString = QLocale::system().toString(markupAnnotation->getCreationDate().toLocalTime(), QLocale::LongFormat);
title = QString("%1 (%2)").arg(title, dateTimeString).trimmed();
groupBox->setStyleSheet(style);
groupBox->setTitle(title);
QVBoxLayout* groupBoxLayout = new QVBoxLayout(groupBox);
QLabel* label = new QLabel(groupBox);
label->setTextInteractionFlags(Qt::TextBrowserInteraction);
label->setWordWrap(true);
label->setText(markupAnnotation->getContents());
label->setFixedWidth(PDFWidgetUtils::scaleDPI_x(label, 250));
label->setMinimumHeight(label->sizeHint().height());
groupBoxLayout->addWidget(label);
}
frameWidget->setFixedSize(frameWidget->minimumSizeHint());
parentWidget->setFixedSize(scrollArea->sizeHint());
}
} // namespace pdf

View File

@@ -0,0 +1,95 @@
// Copyright (C) 2023 Jakub Melka
//
// This file is part of PDF4QT.
//
// PDF4QT is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// with the written consent of the copyright owner, any later version.
//
// PDF4QT is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
#include "pdfannotation.h"
namespace pdf
{
class PDFDrawWidgetProxy;
/// Annotation manager for GUI rendering, it also manages annotations widgets
/// for parent widget.
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFWidgetAnnotationManager : public PDFAnnotationManager, public IDrawWidgetInputInterface
{
Q_OBJECT
private:
using BaseClass = PDFAnnotationManager;
public:
explicit PDFWidgetAnnotationManager(PDFDrawWidgetProxy* proxy, QObject* parent);
virtual ~PDFWidgetAnnotationManager() override;
virtual void setDocument(const PDFModifiedDocument& document) override;
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
/// Returns tooltip generated from annotation
virtual QString getTooltip() const override { return m_tooltip; }
/// Returns current cursor
virtual const std::optional<QCursor>& getCursor() const override { return m_cursor; }
virtual int getInputPriority() const override { return AnnotationPriority; }
signals:
void actionTriggered(const PDFAction* action);
void documentModified(PDFModifiedDocument document);
private:
void updateFromMouseEvent(QMouseEvent* event);
void onShowPopupAnnotation();
void onCopyAnnotation();
void onEditAnnotation();
void onDeleteAnnotation();
/// Creates dialog for markup annotations. This function is used only for markup annotations,
/// do not use them for other annotations (function can crash).
/// \param widget Dialog's parent widget
/// \param pageAnnotation Markup annotation
/// \param pageAnnotations Page annotations
QDialog* createDialogForMarkupAnnotations(PDFWidget* widget,
const PageAnnotation& pageAnnotation,
const PageAnnotations& pageAnnotations);
/// Creates widgets for markup annotation main popup widget. Also sets
/// default size of parent widget.
/// \param parentWidget Parent widget, where widgets are created
/// \param pageAnnotation Markup annotation
/// \param pageAnnotations Page annotations
void createWidgetsForMarkupAnnotations(QWidget* parentWidget,
const PageAnnotation& pageAnnotation,
const PageAnnotations& pageAnnotations);
PDFDrawWidgetProxy* m_proxy;
QString m_tooltip;
std::optional<QCursor> m_cursor;
QPoint m_editableAnnotationGlobalPosition; ///< Position, where action on annotation was executed
PDFObjectReference m_editableAnnotation; ///< Annotation to be edited or deleted
PDFObjectReference m_editableAnnotationPage; ///< Page of annotation above
};
} // namespace pdf

View File

@@ -34,7 +34,7 @@ namespace pdf
/// Base class for various widget tools (for example, searching, text selection,
/// screenshots, zoom tool etc.). Each tool can have subtools (for example,
/// screenshot tool is picking screenshot rectangle).
class PDF4QTLIBSHARED_EXPORT PDFWidgetTool : public QObject, public IDocumentDrawInterface
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFWidgetTool : public QObject, public IDocumentDrawInterface
{
Q_OBJECT
@@ -273,7 +273,7 @@ private:
};
/// Tool to magnify specific area in the drawing widget
class PDF4QTLIBSHARED_EXPORT PDFMagnifierTool : public PDFWidgetTool
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFMagnifierTool : public PDFWidgetTool
{
Q_OBJECT
@@ -308,7 +308,7 @@ private:
};
/// Tools for picking various items on page - points, rectangles, images etc.
class PDF4QTLIBSHARED_EXPORT PDFPickTool : public PDFWidgetTool
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFPickTool : public PDFWidgetTool
{
Q_OBJECT
@@ -433,7 +433,7 @@ private:
/// Tool that makes screenshot of page area and copies it to the clipboard,
/// using current client area to determine image size.
class PDF4QTLIBSHARED_EXPORT PDFScreenshotTool : public PDFWidgetTool
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFScreenshotTool : public PDFWidgetTool
{
Q_OBJECT
@@ -455,7 +455,7 @@ private:
/// Tool that extracts image from page and copies it to the clipboard,
/// using image original size (not zoomed size from widget area)
class PDF4QTLIBSHARED_EXPORT PDFExtractImageTool : public PDFWidgetTool
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFExtractImageTool : public PDFWidgetTool
{
Q_OBJECT
@@ -481,7 +481,7 @@ private:
/// Manager used for managing tools, their activity, availability
/// and other settings. It also defines a predefined set of tools,
/// available for various purposes (text searching, magnifier tool etc.)
class PDF4QTLIBSHARED_EXPORT PDFToolManager : public QObject, public IDrawWidgetInputInterface
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFToolManager : public QObject, public IDrawWidgetInputInterface
{
Q_OBJECT

View File

@@ -26,7 +26,7 @@
namespace pdf
{
class PDF4QTLIBSHARED_EXPORT PDFWidgetUtils
class PDF4QTLIBWIDGETSSHARED_EXPORT PDFWidgetUtils
{
public:
PDFWidgetUtils() = delete;