File indexing completed on 2024-05-19 08:24:36
0001 /* 0002 SPDX-FileCopyrightText: 2005 Enrico Ros <eros.kde@email.it> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "annotationtools.h" 0008 0009 // qt / kde includes 0010 #include <QColor> 0011 #include <QCursor> 0012 #include <QEvent> 0013 #include <QMouseEvent> 0014 0015 // local includes 0016 #include "core/annotations.h" 0017 0018 AnnotatorEngine::AnnotatorEngine(const QDomElement &engineElement) 0019 : m_engineElement(engineElement) 0020 , m_creationCompleted(false) 0021 , m_item(nullptr) 0022 { 0023 // parse common engine attributes 0024 if (engineElement.hasAttribute(QStringLiteral("color"))) { 0025 m_engineColor = QColor(engineElement.attribute(QStringLiteral("color"))); 0026 } 0027 0028 // get the annotation element 0029 QDomElement annElement = m_engineElement.firstChild().toElement(); 0030 if (!annElement.isNull() && annElement.tagName() == QLatin1String("annotation")) { 0031 m_annotElement = annElement; 0032 } 0033 } 0034 0035 void AnnotatorEngine::decodeEvent(const QMouseEvent *mouseEvent, EventType *eventType, Button *button) 0036 { 0037 *eventType = AnnotatorEngine::Press; 0038 if (mouseEvent->type() == QEvent::MouseMove) { 0039 *eventType = AnnotatorEngine::Move; 0040 } else if (mouseEvent->type() == QEvent::MouseButtonRelease) { 0041 *eventType = AnnotatorEngine::Release; 0042 } 0043 0044 *button = AnnotatorEngine::None; 0045 const Qt::MouseButtons buttonState = (*eventType == AnnotatorEngine::Move) ? mouseEvent->buttons() : mouseEvent->button(); 0046 if (buttonState == Qt::LeftButton) { 0047 *button = AnnotatorEngine::Left; 0048 } else if (buttonState == Qt::RightButton) { 0049 *button = AnnotatorEngine::Right; 0050 } 0051 } 0052 0053 void AnnotatorEngine::decodeEvent(const QTabletEvent *tabletEvent, EventType *eventType, Button *button) 0054 { 0055 switch (tabletEvent->type()) { 0056 case QEvent::TabletPress: 0057 // Tablet press event is equivalent to pressing the left mouse button 0058 *button = AnnotatorEngine::Left; 0059 *eventType = AnnotatorEngine::Press; 0060 break; 0061 case QEvent::TabletRelease: 0062 // Tablet release event is equivalent to releasing the left mouse button 0063 *button = AnnotatorEngine::Left; 0064 *eventType = AnnotatorEngine::Release; 0065 break; 0066 case QEvent::TabletMove: 0067 // Tablet events are only routed if the pen is down so 0068 // this is equivalent to the left mouse button being pressed 0069 *button = AnnotatorEngine::Left; 0070 *eventType = AnnotatorEngine::Move; 0071 break; 0072 default: 0073 Q_ASSERT(false); 0074 break; 0075 } 0076 } 0077 0078 AnnotatorEngine::~AnnotatorEngine() 0079 { 0080 } 0081 0082 QCursor AnnotatorEngine::cursor() const 0083 { 0084 return Qt::CrossCursor; 0085 } 0086 0087 SmoothPath::SmoothPath(const QList<Okular::NormalizedPoint> &points, const QPen &pen, qreal opacity, QPainter::CompositionMode compositionMode) 0088 : points(points) 0089 , pen(pen) 0090 , opacity(opacity) 0091 , compositionMode(compositionMode) 0092 { 0093 } 0094 0095 /** SmoothPathEngine */ 0096 SmoothPathEngine::SmoothPathEngine(const QDomElement &engineElement) 0097 : AnnotatorEngine(engineElement) 0098 , compositionMode(QPainter::CompositionMode_SourceOver) 0099 { 0100 // parse engine specific attributes 0101 if (engineElement.attribute(QStringLiteral("compositionMode"), QStringLiteral("sourceOver")) == QLatin1String("clear")) { 0102 compositionMode = QPainter::CompositionMode_Clear; 0103 } 0104 } 0105 0106 QRect SmoothPathEngine::event(EventType type, Button button, Modifiers /*modifiers*/, double nX, double nY, double xScale, double yScale, const Okular::Page * /*page*/) 0107 { 0108 // only proceed if pressing left button 0109 if (button != Left) { 0110 return QRect(); 0111 } 0112 0113 // start operation 0114 if (type == Press && points.isEmpty()) { 0115 lastPoint.x = nX; 0116 lastPoint.y = nY; 0117 totalRect.left = totalRect.right = lastPoint.x; 0118 totalRect.top = totalRect.bottom = lastPoint.y; 0119 points.append(lastPoint); 0120 } 0121 // add a point to the path 0122 else if (type == Move && points.count() > 0) { 0123 // double dist = hypot( nX - points.last().x, nY - points.last().y ); 0124 // if ( dist > 0.0001 ) 0125 //{ 0126 // append mouse position (as normalized point) to the list 0127 Okular::NormalizedPoint nextPoint = Okular::NormalizedPoint(nX, nY); 0128 points.append(nextPoint); 0129 // update total rect 0130 totalRect.left = qMin(totalRect.left, nX); 0131 totalRect.top = qMin(totalRect.top, nY); 0132 totalRect.right = qMax(nX, totalRect.right); 0133 totalRect.bottom = qMax(nY, totalRect.bottom); 0134 // paint the difference to previous full rect 0135 Okular::NormalizedRect incrementalRect; 0136 incrementalRect.left = qMin(nextPoint.x, lastPoint.x); 0137 incrementalRect.right = qMax(nextPoint.x, lastPoint.x); 0138 incrementalRect.top = qMin(nextPoint.y, lastPoint.y); 0139 incrementalRect.bottom = qMax(nextPoint.y, lastPoint.y); 0140 lastPoint = nextPoint; 0141 return incrementalRect.geometry((int)xScale, (int)yScale); 0142 //} 0143 } 0144 // terminate process 0145 else if (type == Release && points.count() > 0) { 0146 if (points.count() < 2) { 0147 points.clear(); 0148 } else { 0149 m_creationCompleted = true; 0150 } 0151 return totalRect.geometry((int)xScale, (int)yScale); 0152 } 0153 return QRect(); 0154 } 0155 0156 void SmoothPathEngine::paint(QPainter *painter, double xScale, double yScale, const QRect & /*clipRect*/) 0157 { 0158 const double penWidth = m_annotElement.attribute(QStringLiteral("width"), QStringLiteral("1")).toInt(); 0159 const qreal opacity = m_annotElement.attribute(QStringLiteral("opacity"), QStringLiteral("1.0")).toDouble(); 0160 0161 // use engine's color for painting 0162 const SmoothPath path(points, QPen(m_engineColor, penWidth), opacity, compositionMode); 0163 0164 // draw the path 0165 path.paint(painter, xScale, yScale); 0166 } 0167 0168 void SmoothPath::paint(QPainter *painter, double xScale, double yScale) const 0169 { 0170 // draw SmoothPaths with at least 2 points 0171 if (points.count() > 1) { 0172 painter->setCompositionMode(compositionMode); 0173 painter->setPen(pen); 0174 painter->setOpacity(opacity); 0175 0176 QPainterPath path; 0177 QList<Okular::NormalizedPoint>::const_iterator pIt = points.begin(), pEnd = points.end(); 0178 path.moveTo(QPointF(pIt->x * xScale, pIt->y * yScale)); 0179 ++pIt; 0180 for (; pIt != pEnd; ++pIt) { 0181 path.lineTo(QPointF(pIt->x * xScale, pIt->y * yScale)); 0182 } 0183 painter->drawPath(path); 0184 } 0185 } 0186 0187 QList<Okular::Annotation *> SmoothPathEngine::end() 0188 { 0189 m_creationCompleted = false; 0190 0191 // find out annotation's description node 0192 if (m_annotElement.isNull()) { 0193 return QList<Okular::Annotation *>(); 0194 } 0195 0196 // find out annotation's type 0197 Okular::Annotation *ann = nullptr; 0198 QString typeString = m_annotElement.attribute(QStringLiteral("type")); 0199 0200 // create InkAnnotation from path 0201 if (typeString == QLatin1String("Ink")) { 0202 Okular::InkAnnotation *ia = new Okular::InkAnnotation(); 0203 ann = ia; 0204 if (m_annotElement.hasAttribute(QStringLiteral("width"))) { 0205 ann->style().setWidth(m_annotElement.attribute(QStringLiteral("width")).toDouble()); 0206 } 0207 // fill points 0208 QList<QList<Okular::NormalizedPoint>> list = ia->inkPaths(); 0209 list.append(points); 0210 ia->setInkPaths(list); 0211 // set boundaries 0212 ia->setBoundingRectangle(totalRect); 0213 } 0214 0215 // safety check 0216 if (!ann) { 0217 return QList<Okular::Annotation *>(); 0218 } 0219 0220 // set common attributes 0221 ann->style().setColor(m_annotElement.hasAttribute(QStringLiteral("color")) ? m_annotElement.attribute(QStringLiteral("color")) : m_engineColor); 0222 if (m_annotElement.hasAttribute(QStringLiteral("opacity"))) { 0223 ann->style().setOpacity(m_annotElement.attribute(QStringLiteral("opacity"), QStringLiteral("1.0")).toDouble()); 0224 } 0225 0226 // return annotation 0227 return QList<Okular::Annotation *>() << ann; 0228 } 0229 0230 SmoothPath SmoothPathEngine::endSmoothPath() 0231 { 0232 m_creationCompleted = false; 0233 0234 QColor color(m_annotElement.hasAttribute(QStringLiteral("color")) ? m_annotElement.attribute(QStringLiteral("color")) : m_engineColor); 0235 0236 const int width = m_annotElement.attribute(QStringLiteral("width"), QStringLiteral("2")).toInt(); 0237 const qreal opacity = m_annotElement.attribute(QStringLiteral("opacity"), QStringLiteral("1.0")).toDouble(); 0238 0239 return SmoothPath(points, QPen(color, width), opacity, compositionMode); 0240 } 0241 0242 /* kate: replace-tabs on; indent-width 4; */