/*************************************************************************** * Copyright (C) 2005 by Enrico Ros * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "annotationtools.h" // qt / kde includes #include #include #include #include // 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 &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, 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::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 > 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; */