You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
8.3 KiB
233 lines
8.3 KiB
/* |
|
SPDX-FileCopyrightText: 2005 Enrico Ros <eros.kde@email.it> |
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "annotationtools.h" |
|
|
|
// qt / kde includes |
|
#include <QColor> |
|
#include <QCursor> |
|
#include <QEvent> |
|
#include <QMouseEvent> |
|
|
|
// local includes |
|
#include "core/annotations.h" |
|
|
|
AnnotatorEngine::AnnotatorEngine(const QDomElement &engineElement) |
|
: m_engineElement(engineElement) |
|
, m_creationCompleted(false) |
|
, m_item(nullptr) |
|
{ |
|
// parse common engine attributes |
|
if (engineElement.hasAttribute(QStringLiteral("color"))) |
|
m_engineColor = QColor(engineElement.attribute(QStringLiteral("color"))); |
|
|
|
// get the annotation element |
|
QDomElement annElement = m_engineElement.firstChild().toElement(); |
|
if (!annElement.isNull() && annElement.tagName() == QLatin1String("annotation")) |
|
m_annotElement = annElement; |
|
} |
|
|
|
void AnnotatorEngine::decodeEvent(const QMouseEvent *mouseEvent, EventType *eventType, Button *button) |
|
{ |
|
*eventType = AnnotatorEngine::Press; |
|
if (mouseEvent->type() == QEvent::MouseMove) |
|
*eventType = AnnotatorEngine::Move; |
|
else if (mouseEvent->type() == QEvent::MouseButtonRelease) |
|
*eventType = AnnotatorEngine::Release; |
|
|
|
*button = AnnotatorEngine::None; |
|
const Qt::MouseButtons buttonState = (*eventType == AnnotatorEngine::Move) ? mouseEvent->buttons() : mouseEvent->button(); |
|
if (buttonState == Qt::LeftButton) |
|
*button = AnnotatorEngine::Left; |
|
else if (buttonState == Qt::RightButton) |
|
*button = AnnotatorEngine::Right; |
|
} |
|
|
|
void AnnotatorEngine::decodeEvent(const QTabletEvent *tabletEvent, EventType *eventType, Button *button) |
|
{ |
|
switch (tabletEvent->type()) { |
|
case QEvent::TabletPress: |
|
// Tablet press event is equivalent to pressing the left mouse button |
|
*button = AnnotatorEngine::Left; |
|
*eventType = AnnotatorEngine::Press; |
|
break; |
|
case QEvent::TabletRelease: |
|
// Tablet release event is equivalent to releasing the left mouse button |
|
*button = AnnotatorEngine::Left; |
|
*eventType = AnnotatorEngine::Release; |
|
break; |
|
case QEvent::TabletMove: |
|
// Tablet events are only routed if the pen is down so |
|
// this is equivalent to the left mouse button being pressed |
|
*button = AnnotatorEngine::Left; |
|
*eventType = AnnotatorEngine::Move; |
|
break; |
|
default: |
|
Q_ASSERT(false); |
|
break; |
|
} |
|
} |
|
|
|
AnnotatorEngine::~AnnotatorEngine() |
|
{ |
|
} |
|
|
|
QCursor AnnotatorEngine::cursor() const |
|
{ |
|
return Qt::CrossCursor; |
|
} |
|
|
|
SmoothPath::SmoothPath(const QLinkedList<Okular::NormalizedPoint> &points, const QPen &pen, qreal opacity, QPainter::CompositionMode compositionMode) |
|
: points(points) |
|
, pen(pen) |
|
, opacity(opacity) |
|
, compositionMode(compositionMode) |
|
{ |
|
} |
|
|
|
/** SmoothPathEngine */ |
|
SmoothPathEngine::SmoothPathEngine(const QDomElement &engineElement) |
|
: AnnotatorEngine(engineElement) |
|
, compositionMode(QPainter::CompositionMode_SourceOver) |
|
{ |
|
// parse engine specific attributes |
|
if (engineElement.attribute(QStringLiteral("compositionMode"), QStringLiteral("sourceOver")) == QLatin1String("clear")) |
|
compositionMode = QPainter::CompositionMode_Clear; |
|
} |
|
|
|
QRect SmoothPathEngine::event(EventType type, Button button, Modifiers /*modifiers*/, double nX, double nY, double xScale, double yScale, const Okular::Page * /*page*/) |
|
{ |
|
// only proceed if pressing left button |
|
if (button != Left) |
|
return QRect(); |
|
|
|
// start operation |
|
if (type == Press && points.isEmpty()) { |
|
lastPoint.x = nX; |
|
lastPoint.y = nY; |
|
totalRect.left = totalRect.right = lastPoint.x; |
|
totalRect.top = totalRect.bottom = lastPoint.y; |
|
points.append(lastPoint); |
|
} |
|
// add a point to the path |
|
else if (type == Move && points.count() > 0) { |
|
// double dist = hypot( nX - points.last().x, nY - points.last().y ); |
|
// if ( dist > 0.0001 ) |
|
//{ |
|
// append mouse position (as normalized point) to the list |
|
Okular::NormalizedPoint nextPoint = Okular::NormalizedPoint(nX, nY); |
|
points.append(nextPoint); |
|
// update total rect |
|
double dX = 2.0 / (double)xScale; |
|
double dY = 2.0 / (double)yScale; |
|
totalRect.left = qMin(totalRect.left, nX - dX); |
|
totalRect.top = qMin(totalRect.top, nY - dY); |
|
totalRect.right = qMax(nX + dX, totalRect.right); |
|
totalRect.bottom = qMax(nY + dY, totalRect.bottom); |
|
// paint the difference to previous full rect |
|
Okular::NormalizedRect incrementalRect; |
|
incrementalRect.left = qMin(nextPoint.x, lastPoint.x) - dX; |
|
incrementalRect.right = qMax(nextPoint.x, lastPoint.x) + dX; |
|
incrementalRect.top = qMin(nextPoint.y, lastPoint.y) - dY; |
|
incrementalRect.bottom = qMax(nextPoint.y, lastPoint.y) + dY; |
|
lastPoint = nextPoint; |
|
return incrementalRect.geometry((int)xScale, (int)yScale); |
|
//} |
|
} |
|
// terminate process |
|
else if (type == Release && points.count() > 0) { |
|
if (points.count() < 2) |
|
points.clear(); |
|
else |
|
m_creationCompleted = true; |
|
return totalRect.geometry((int)xScale, (int)yScale); |
|
} |
|
return QRect(); |
|
} |
|
|
|
void SmoothPathEngine::paint(QPainter *painter, double xScale, double yScale, const QRect & /*clipRect*/) |
|
{ |
|
const double penWidth = m_annotElement.attribute(QStringLiteral("width"), QStringLiteral("1")).toInt(); |
|
const qreal opacity = m_annotElement.attribute(QStringLiteral("opacity"), QStringLiteral("1.0")).toDouble(); |
|
|
|
// use engine's color for painting |
|
const SmoothPath path(points, QPen(m_engineColor, penWidth), opacity, compositionMode); |
|
|
|
// draw the path |
|
path.paint(painter, xScale, yScale); |
|
} |
|
|
|
void SmoothPath::paint(QPainter *painter, double xScale, double yScale) const |
|
{ |
|
// draw SmoothPaths with at least 2 points |
|
if (points.count() > 1) { |
|
painter->setCompositionMode(compositionMode); |
|
painter->setPen(pen); |
|
painter->setOpacity(opacity); |
|
|
|
QPainterPath path; |
|
QLinkedList<Okular::NormalizedPoint>::const_iterator pIt = points.begin(), pEnd = points.end(); |
|
path.moveTo(QPointF(pIt->x * xScale, pIt->y * yScale)); |
|
++pIt; |
|
for (; pIt != pEnd; ++pIt) { |
|
path.lineTo(QPointF(pIt->x * xScale, pIt->y * yScale)); |
|
} |
|
painter->drawPath(path); |
|
} |
|
} |
|
|
|
QList<Okular::Annotation *> SmoothPathEngine::end() |
|
{ |
|
m_creationCompleted = false; |
|
|
|
// find out annotation's description node |
|
if (m_annotElement.isNull()) |
|
return QList<Okular::Annotation *>(); |
|
|
|
// find out annotation's type |
|
Okular::Annotation *ann = nullptr; |
|
QString typeString = m_annotElement.attribute(QStringLiteral("type")); |
|
|
|
// create InkAnnotation from path |
|
if (typeString == QLatin1String("Ink")) { |
|
Okular::InkAnnotation *ia = new Okular::InkAnnotation(); |
|
ann = ia; |
|
if (m_annotElement.hasAttribute(QStringLiteral("width"))) |
|
ann->style().setWidth(m_annotElement.attribute(QStringLiteral("width")).toDouble()); |
|
// fill points |
|
QList<QLinkedList<Okular::NormalizedPoint>> list = ia->inkPaths(); |
|
list.append(points); |
|
ia->setInkPaths(list); |
|
// set boundaries |
|
ia->setBoundingRectangle(totalRect); |
|
} |
|
|
|
// safety check |
|
if (!ann) |
|
return QList<Okular::Annotation *>(); |
|
|
|
// set common attributes |
|
ann->style().setColor(m_annotElement.hasAttribute(QStringLiteral("color")) ? m_annotElement.attribute(QStringLiteral("color")) : m_engineColor); |
|
if (m_annotElement.hasAttribute(QStringLiteral("opacity"))) |
|
ann->style().setOpacity(m_annotElement.attribute(QStringLiteral("opacity"), QStringLiteral("1.0")).toDouble()); |
|
|
|
// return annotation |
|
return QList<Okular::Annotation *>() << ann; |
|
} |
|
|
|
SmoothPath SmoothPathEngine::endSmoothPath() |
|
{ |
|
m_creationCompleted = false; |
|
|
|
QColor color(m_annotElement.hasAttribute(QStringLiteral("color")) ? m_annotElement.attribute(QStringLiteral("color")) : m_engineColor); |
|
|
|
const int width = m_annotElement.attribute(QStringLiteral("width"), QStringLiteral("2")).toInt(); |
|
const qreal opacity = m_annotElement.attribute(QStringLiteral("opacity"), QStringLiteral("1.0")).toDouble(); |
|
|
|
return SmoothPath(points, QPen(color, width), opacity, compositionMode); |
|
} |
|
|
|
/* kate: replace-tabs on; indent-width 4; */
|
|
|