diff --git a/conf/editannottooldialog.cpp b/conf/editannottooldialog.cpp index 64a01ab59..77bd1ba6a 100644 --- a/conf/editannottooldialog.cpp +++ b/conf/editannottooldialog.cpp @@ -1,500 +1,500 @@ /*************************************************************************** * Copyright (C) 2012 by Fabio D'Urso * * Copyright (C) 2015 by Laurent Montel * * * * 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 "editannottooldialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/annotations.h" #include "ui/annotationwidgets.h" #include "ui/pageviewannotator.h" EditAnnotToolDialog::EditAnnotToolDialog( QWidget *parent, const QDomElement &initialState ) : QDialog( parent ), m_stubann( nullptr ), m_annotationWidget( nullptr ) { QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &EditAnnotToolDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &EditAnnotToolDialog::reject); okButton->setDefault(true); QLabel * tmplabel; QWidget *widget = new QWidget( this ); QGridLayout * widgetLayout = new QGridLayout( widget ); mainLayout->addWidget(widget); mainLayout->addWidget(buttonBox); m_name = new KLineEdit( widget ); mainLayout->addWidget(m_name); tmplabel = new QLabel( i18n( "&Name:" ), widget ); mainLayout->addWidget(tmplabel); tmplabel->setBuddy( m_name ); widgetLayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); widgetLayout->addWidget( m_name, 0, 1 ); m_type = new KComboBox( false, widget ); mainLayout->addWidget(m_type); connect(m_type, static_cast(&KComboBox::currentIndexChanged), this, &EditAnnotToolDialog::slotTypeChanged); tmplabel = new QLabel( i18n( "&Type:" ), widget ); mainLayout->addWidget(tmplabel); tmplabel->setBuddy( m_type ); widgetLayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); widgetLayout->addWidget( m_type, 1, 1 ); m_toolIcon = new QLabel( widget ); mainLayout->addWidget(m_toolIcon); m_toolIcon->setAlignment( Qt::AlignRight | Qt::AlignTop ); m_toolIcon->setMinimumSize( 40, 32 ); widgetLayout->addWidget( m_toolIcon, 0, 2, 2, 1 ); m_appearanceBox = new QGroupBox( i18n( "Appearance" ), widget ); mainLayout->addWidget(m_appearanceBox); m_appearanceBox->setLayout( new QVBoxLayout( m_appearanceBox ) ); widgetLayout->addWidget( m_appearanceBox, 2, 0, 1, 3 ); // Populate combobox with annotation types m_type->addItem( i18n("Pop-up Note"), qVariantFromValue( ToolNoteLinked ) ); m_type->addItem( i18n("Inline Note"), qVariantFromValue( ToolNoteInline ) ); m_type->addItem( i18n("Freehand Line"), qVariantFromValue( ToolInk ) ); m_type->addItem( i18n("Straight Line"), qVariantFromValue( ToolStraightLine ) ); m_type->addItem( i18n("Polygon"), qVariantFromValue( ToolPolygon ) ); m_type->addItem( i18n("Text markup"), qVariantFromValue( ToolTextMarkup ) ); m_type->addItem( i18n("Geometrical shape"), qVariantFromValue( ToolGeometricalShape ) ); m_type->addItem( i18n("Stamp"), qVariantFromValue( ToolStamp ) ); createStubAnnotation(); if ( initialState.isNull() ) { setWindowTitle( i18n("Create annotation tool") ); } else { setWindowTitle( i18n("Edit annotation tool") ); loadTool( initialState ); } rebuildAppearanceBox(); updateDefaultNameAndIcon(); } EditAnnotToolDialog::~EditAnnotToolDialog() { delete m_annotationWidget; } QString EditAnnotToolDialog::name() const { return m_name->text(); } QDomDocument EditAnnotToolDialog::toolXml() const { const ToolType toolType = m_type->itemData( m_type->currentIndex() ).value(); QDomDocument doc; QDomElement toolElement = doc.createElement( QStringLiteral("tool") ); QDomElement engineElement = doc.createElement( QStringLiteral("engine") ); QDomElement annotationElement = doc.createElement( QStringLiteral("annotation") ); doc.appendChild( toolElement ); toolElement.appendChild( engineElement ); engineElement.appendChild( annotationElement ); - const QString color = m_stubann->style().color().name(); + const QString color = m_stubann->style().color().name( QColor::HexArgb ); const QString opacity = QString::number( m_stubann->style().opacity() ); const QString width = QString::number( m_stubann->style().width() ); if ( toolType == ToolNoteLinked ) { Okular::TextAnnotation * ta = static_cast( m_stubann ); toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("note-linked") ); engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PickPoint") ); engineElement.setAttribute( QStringLiteral("color"), color ); engineElement.setAttribute( QStringLiteral("hoverIcon"), QStringLiteral("tool-note") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Text") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("icon"), ta->textIcon() ); } else if ( toolType == ToolNoteInline ) { Okular::TextAnnotation * ta = static_cast( m_stubann ); toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("note-inline") ); engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PickPoint") ); engineElement.setAttribute( QStringLiteral("color"), color ); engineElement.setAttribute( QStringLiteral("hoverIcon"), QStringLiteral("tool-note-inline") ); engineElement.setAttribute( QStringLiteral("block"), QStringLiteral("true") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("FreeText") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("width"), width ); if ( ta->inplaceAlignment() != 0 ) annotationElement.setAttribute( QStringLiteral("align"), ta->inplaceAlignment() ); if ( ta->textFont() != QApplication::font() ) annotationElement.setAttribute( QStringLiteral("font"), ta->textFont().toString() ); } else if ( toolType == ToolInk ) { toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("ink") ); engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("SmoothLine") ); engineElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Ink") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("width"), width ); } else if ( toolType == ToolStraightLine ) { Okular::LineAnnotation * la = static_cast( m_stubann ); toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("straight-line") ); engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PolyLine") ); engineElement.setAttribute( QStringLiteral("color"), color ); engineElement.setAttribute( QStringLiteral("points"), QStringLiteral("2") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Line") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("width"), width ); if ( la->lineLeadingForwardPoint() != 0 || la->lineLeadingBackwardPoint() != 0 ) { annotationElement.setAttribute( QStringLiteral("leadFwd"), QString::number( la->lineLeadingForwardPoint() ) ); annotationElement.setAttribute( QStringLiteral("leadBack"), QString::number( la->lineLeadingBackwardPoint() ) ); } } else if ( toolType == ToolPolygon ) { Okular::LineAnnotation * la = static_cast( m_stubann ); toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("polygon") ); engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PolyLine") ); engineElement.setAttribute( QStringLiteral("color"), color ); engineElement.setAttribute( QStringLiteral("points"), QStringLiteral("-1") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Line") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("width"), width ); if ( la->lineInnerColor().isValid() ) { annotationElement.setAttribute( QStringLiteral("innerColor"), la->lineInnerColor().name() ); } } else if ( toolType == ToolTextMarkup ) { Okular::HighlightAnnotation * ha = static_cast( m_stubann ); switch ( ha->highlightType() ) { case Okular::HighlightAnnotation::Highlight: toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("highlight") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Highlight") ); break; case Okular::HighlightAnnotation::Squiggly: toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("squiggly") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Squiggly") ); break; case Okular::HighlightAnnotation::Underline: toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("underline") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Underline") ); break; case Okular::HighlightAnnotation::StrikeOut: toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("strikeout") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("StrikeOut") ); break; } engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("TextSelector") ); engineElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("color"), color ); } else if ( toolType == ToolGeometricalShape ) { Okular::GeomAnnotation * ga = static_cast( m_stubann ); if ( ga->geometricalType() == Okular::GeomAnnotation::InscribedCircle ) { toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("ellipse") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("GeomCircle") ); } else { toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("rectangle") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("GeomSquare") ); } engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PickPoint") ); engineElement.setAttribute( QStringLiteral("color"), color ); engineElement.setAttribute( QStringLiteral("block"), QStringLiteral("true") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("width"), width ); if ( ga->geometricalInnerColor().isValid() ) annotationElement.setAttribute( QStringLiteral("innerColor"), ga->geometricalInnerColor().name() ); } else if ( toolType == ToolStamp ) { Okular::StampAnnotation * sa = static_cast( m_stubann ); toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("stamp") ); engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PickPoint") ); engineElement.setAttribute( QStringLiteral("hoverIcon"), sa->stampIconName() ); engineElement.setAttribute( QStringLiteral("size"), QStringLiteral("64") ); engineElement.setAttribute( QStringLiteral("block"), QStringLiteral("true") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Stamp") ); annotationElement.setAttribute( QStringLiteral("icon"), sa->stampIconName() ); } if ( opacity != QStringLiteral("1") ) annotationElement.setAttribute( QStringLiteral("opacity"), opacity ); return doc; } void EditAnnotToolDialog::createStubAnnotation() { const ToolType toolType = m_type->itemData( m_type->currentIndex() ).value(); // Delete previous stub annotation, if any delete m_stubann; // Create stub annotation if ( toolType == ToolNoteLinked ) { Okular::TextAnnotation * ta = new Okular::TextAnnotation(); ta->setTextType( Okular::TextAnnotation::Linked ); ta->setTextIcon( QStringLiteral("Note") ); ta->style().setColor( Qt::yellow ); m_stubann = ta; } else if ( toolType == ToolNoteInline ) { Okular::TextAnnotation * ta = new Okular::TextAnnotation(); ta->setTextType( Okular::TextAnnotation::InPlace ); ta->style().setWidth( 1.0 ); ta->style().setColor( Qt::yellow ); m_stubann = ta; } else if ( toolType == ToolInk ) { m_stubann = new Okular::InkAnnotation(); m_stubann->style().setWidth( 2.0 ); m_stubann->style().setColor( Qt::green ); } else if ( toolType == ToolStraightLine ) { Okular::LineAnnotation * la = new Okular::LineAnnotation(); la->setLinePoints( QLinkedList() << Okular::NormalizedPoint(0,0) << Okular::NormalizedPoint(1,0) ); la->style().setColor( QColor( 0xff, 0xe0, 0x00 ) ); m_stubann = la; } else if ( toolType == ToolPolygon ) { Okular::LineAnnotation * la = new Okular::LineAnnotation(); la->setLinePoints( QLinkedList() << Okular::NormalizedPoint(0,0) << Okular::NormalizedPoint(1,0) << Okular::NormalizedPoint(1,1) ); la->setLineClosed( true ); la->style().setColor( QColor( 0x00, 0x7e, 0xee ) ); m_stubann = la; } else if ( toolType == ToolTextMarkup ) { m_stubann = new Okular::HighlightAnnotation(); m_stubann->style().setColor( Qt::yellow ); } else if ( toolType == ToolGeometricalShape ) { Okular::GeomAnnotation * ga = new Okular::GeomAnnotation(); ga->setGeometricalType( Okular::GeomAnnotation::InscribedCircle ); ga->style().setWidth( 5.0 ); ga->style().setColor( Qt::cyan ); m_stubann = ga; } else if ( toolType == ToolStamp ) { Okular::StampAnnotation * sa = new Okular::StampAnnotation(); sa->setStampIconName( QStringLiteral("okular") ); m_stubann = sa; } } void EditAnnotToolDialog::rebuildAppearanceBox() { // Remove previous widget (if any) if ( m_annotationWidget ) { delete m_annotationWidget->appearanceWidget(); delete m_annotationWidget; } m_annotationWidget = AnnotationWidgetFactory::widgetFor( m_stubann ); m_appearanceBox->layout()->addWidget( m_annotationWidget->appearanceWidget() ); connect(m_annotationWidget, &AnnotationWidget::dataChanged, this, &EditAnnotToolDialog::slotDataChanged); } void EditAnnotToolDialog::updateDefaultNameAndIcon() { QDomDocument doc = toolXml(); QDomElement toolElement = doc.documentElement(); m_name->setPlaceholderText( PageViewAnnotator::defaultToolName( toolElement ) ); m_toolIcon->setPixmap( PageViewAnnotator::makeToolPixmap( toolElement ) ); } void EditAnnotToolDialog::setToolType( ToolType newType ) { int idx = -1; for ( int i = 0; idx == -1 && i < m_type->count(); ++i ) { if ( m_type->itemData( i ).value() == newType ) idx = i; } // The following call also results in createStubAnnotation being called m_type->setCurrentIndex( idx ); } void EditAnnotToolDialog::loadTool( const QDomElement &toolElement ) { const QDomElement engineElement = toolElement.elementsByTagName( QStringLiteral("engine") ).item( 0 ).toElement(); const QDomElement annotationElement = engineElement.elementsByTagName( QStringLiteral("annotation") ).item( 0 ).toElement(); const QString annotType = toolElement.attribute( QStringLiteral("type") ); if ( annotType == QLatin1String("ellipse") ) { setToolType( ToolGeometricalShape ); Okular::GeomAnnotation * ga = static_cast( m_stubann ); ga->setGeometricalType( Okular::GeomAnnotation::InscribedCircle ); if ( annotationElement.hasAttribute( QStringLiteral("innerColor") ) ) ga->setGeometricalInnerColor( QColor( annotationElement.attribute( QStringLiteral("innerColor") ) ) ); } else if ( annotType == QLatin1String("highlight") ) { setToolType( ToolTextMarkup ); Okular::HighlightAnnotation * ha = static_cast( m_stubann ); ha->setHighlightType( Okular::HighlightAnnotation::Highlight ); } else if ( annotType == QLatin1String("ink") ) { setToolType( ToolInk ); } else if ( annotType == QLatin1String("note-inline") ) { setToolType( ToolNoteInline ); Okular::TextAnnotation * ta = static_cast( m_stubann ); if ( annotationElement.hasAttribute( QStringLiteral("align") ) ) ta->setInplaceAlignment( annotationElement.attribute( QStringLiteral("align") ).toInt() ); if ( annotationElement.hasAttribute( QStringLiteral("font") ) ) { QFont f; f.fromString( annotationElement.attribute( QStringLiteral("font") ) ); ta->setTextFont( f ); } } else if ( annotType == QLatin1String("note-linked") ) { setToolType( ToolNoteLinked ); Okular::TextAnnotation * ta = static_cast( m_stubann ); ta->setTextIcon( annotationElement.attribute( QStringLiteral("icon") ) ); } else if ( annotType == QLatin1String("polygon") ) { setToolType( ToolPolygon ); Okular::LineAnnotation * la = static_cast( m_stubann ); if ( annotationElement.hasAttribute( QStringLiteral("innerColor") ) ) la->setLineInnerColor( QColor( annotationElement.attribute( QStringLiteral("innerColor") ) ) ); } else if ( annotType == QLatin1String("rectangle") ) { setToolType( ToolGeometricalShape ); Okular::GeomAnnotation * ga = static_cast( m_stubann ); ga->setGeometricalType( Okular::GeomAnnotation::InscribedSquare ); if ( annotationElement.hasAttribute( QStringLiteral("innerColor") ) ) ga->setGeometricalInnerColor( QColor( annotationElement.attribute( QStringLiteral("innerColor") ) ) ); } else if ( annotType == QLatin1String("squiggly") ) { setToolType( ToolTextMarkup ); Okular::HighlightAnnotation * ha = static_cast( m_stubann ); ha->setHighlightType( Okular::HighlightAnnotation::Squiggly ); } else if ( annotType == QLatin1String("stamp") ) { setToolType( ToolStamp ); Okular::StampAnnotation * sa = static_cast( m_stubann ); sa->setStampIconName( annotationElement.attribute( QStringLiteral("icon") ) ); } else if ( annotType == QLatin1String("straight-line") ) { setToolType( ToolStraightLine ); Okular::LineAnnotation * la = static_cast( m_stubann ); if ( annotationElement.hasAttribute( QStringLiteral("leadFwd") ) ) la->setLineLeadingForwardPoint( annotationElement.attribute( QStringLiteral("leadFwd") ).toDouble() ); if ( annotationElement.hasAttribute( QStringLiteral("leadBack") ) ) la->setLineLeadingBackwardPoint( annotationElement.attribute( QStringLiteral("leadBack") ).toDouble() ); } else if ( annotType == QLatin1String("strikeout") ) { setToolType( ToolTextMarkup ); Okular::HighlightAnnotation * ha = static_cast( m_stubann ); ha->setHighlightType( Okular::HighlightAnnotation::StrikeOut ); } else if ( annotType == QLatin1String("underline") ) { setToolType( ToolTextMarkup ); Okular::HighlightAnnotation * ha = static_cast( m_stubann ); ha->setHighlightType( Okular::HighlightAnnotation::Underline ); } // Common properties if ( annotationElement.hasAttribute( QStringLiteral("color") ) ) m_stubann->style().setColor( QColor( annotationElement.attribute( QStringLiteral("color") ) ) ); if ( annotationElement.hasAttribute( QStringLiteral("opacity") ) ) m_stubann->style().setOpacity( annotationElement.attribute( QStringLiteral("opacity") ).toDouble() ); if ( annotationElement.hasAttribute( QStringLiteral("width") ) ) m_stubann->style().setWidth( annotationElement.attribute( QStringLiteral("width") ).toDouble() ); if ( toolElement.hasAttribute( QStringLiteral("name") ) ) m_name->setText( toolElement.attribute( QStringLiteral("name") ) ); } void EditAnnotToolDialog::slotTypeChanged() { createStubAnnotation(); rebuildAppearanceBox(); updateDefaultNameAndIcon(); } void EditAnnotToolDialog::slotDataChanged() { // Mirror changes back in the stub annotation m_annotationWidget->applyChanges(); updateDefaultNameAndIcon(); } diff --git a/core/annotations.cpp b/core/annotations.cpp index 3ac567063..b480182e6 100644 --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -1,3101 +1,3101 @@ /*************************************************************************** * 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 "annotations.h" #include "annotations_p.h" // qt/kde includes #include #include // DBL_MAX #include // local includes #include "action.h" #include "document.h" #include "document_p.h" #include "movie.h" #include "page_p.h" #include "sound.h" using namespace Okular; /** * True, if point @p c lies to the left of the vector from @p a to @p b * @internal */ static bool isLeftOfVector( const NormalizedPoint& a, const NormalizedPoint& b, const NormalizedPoint& c ) { //cross product return ( (b.x - a.x) * ( c.y - a.y) - ( b.y - a.y ) * ( c.x - a.x ) ) > 0; } /** * @brief Calculates distance of the given point @p x @p y @p xScale @p yScale to the @p path * * Does piecewise comparison and selects the distance to the closest segment */ static double distanceSqr( double x, double y, double xScale, double yScale, const QLinkedList& path ) { double distance = DBL_MAX; double thisDistance; QLinkedList::const_iterator i = path.constBegin(); NormalizedPoint lastPoint = *i; for (++i; i != path.constEnd(); ++i) { thisDistance = NormalizedPoint::distanceSqr( x, y, xScale, yScale, lastPoint, (*i) ); if ( thisDistance < distance ) distance = thisDistance; lastPoint = *i; } return distance; } /** * Given the squared @p distance from the idealized 0-width line and a pen width @p penWidth, * (not squared!), returns the final distance * * @warning The returned distance is not exact: * We calculate an (exact) squared distance to the ideal (centered) line, and then subtract * the squared width of the pen: * a^2 - b^2 where a = "distance from idealized 0-width line" b = "pen width" * For an exact result, we would want to calculate "(a - b)^2" but that would require * a square root operation because we only know the squared distance a^2. * * However, the approximation is feasible, because: * error = (a-b)^2 - (a^2 - b^2) = -2ab + 2b^2 = 2b(b - a) * Therefore: * lim_{a->b} a^2 - b^2 - a^2 + 2ab - b^2 --> 0 * * In other words, this approximation will estimate the distance to be slightly more than it actually is * for as long as we are far "outside" the line, becoming more accurate the closer we get to the line * boundary. Trivially, it also fulfils (a1 < a2) => ((a1^2 - b^2) < (a2^2 - b^2)) making it monotonic. * "Inside" of the drawn line, the distance is 0 anyway. */ static double strokeDistance( double distance, double penWidth ) { return fmax(distance - pow( penWidth, 2 ), 0); } //BEGIN AnnotationUtils implementation Annotation * AnnotationUtils::createAnnotation( const QDomElement & annElement ) { // safety check on annotation element if ( !annElement.hasAttribute( QStringLiteral("type") ) ) return nullptr; // build annotation of given type Annotation * annotation = nullptr; int typeNumber = annElement.attribute( QStringLiteral("type") ).toInt(); switch ( typeNumber ) { case Annotation::AText: annotation = new TextAnnotation( annElement ); break; case Annotation::ALine: annotation = new LineAnnotation( annElement ); break; case Annotation::AGeom: annotation = new GeomAnnotation( annElement ); break; case Annotation::AHighlight: annotation = new HighlightAnnotation( annElement ); break; case Annotation::AStamp: annotation = new StampAnnotation( annElement ); break; case Annotation::AInk: annotation = new InkAnnotation( annElement ); break; case Annotation::ACaret: annotation = new CaretAnnotation( annElement ); break; } // return created annotation return annotation; } void AnnotationUtils::storeAnnotation( const Annotation * ann, QDomElement & annElement, QDomDocument & document ) { // save annotation's type as element's attribute annElement.setAttribute( QStringLiteral("type"), (uint)ann->subType() ); // append all annotation data as children of this node ann->store( annElement, document ); } QDomElement AnnotationUtils::findChildElement( const QDomNode & parentNode, const QString & name ) { // loop through the whole children and return a 'name' named element QDomNode subNode = parentNode.firstChild(); while( subNode.isElement() ) { QDomElement element = subNode.toElement(); if ( element.tagName() == name ) return element; subNode = subNode.nextSibling(); } // if the name can't be found, return a dummy null element return QDomElement(); } QRect AnnotationUtils::annotationGeometry( const Annotation * ann, double scaledWidth, double scaledHeight ) { const QRect rect = ann->transformedBoundingRectangle().geometry( (int)scaledWidth, (int)scaledHeight ); if ( ann->subType() == Annotation::AText && ( ( (TextAnnotation*)ann )->textType() == TextAnnotation::Linked ) ) { // To be honest i have no clue of why the 24,24 is here, maybe to make sure it's not too small? // But why only for linked text? const QRect rect24 = QRect( (int)( ann->transformedBoundingRectangle().left * scaledWidth ), (int)( ann->transformedBoundingRectangle().top * scaledHeight ), 24, 24 ); return rect24.united(rect); } return rect; } //END AnnotationUtils implementation AnnotationProxy::~AnnotationProxy() { } //BEGIN Annotation implementation class Annotation::Style::Private { public: Private() : m_opacity( 1.0 ), m_width( 1.0 ), m_style( Solid ), m_xCorners( 0.0 ), m_yCorners( 0.0 ), m_marks( 3 ), m_spaces( 0 ), m_effect( NoEffect ), m_effectIntensity( 1.0 ) { } QColor m_color; double m_opacity; double m_width; LineStyle m_style; double m_xCorners; double m_yCorners; int m_marks; int m_spaces; LineEffect m_effect; double m_effectIntensity; }; Annotation::Style::Style() : d( new Private ) { } Annotation::Style::~Style() { delete d; } Annotation::Style::Style( const Style &other ) : d( new Private ) { *d = *other.d; } Annotation::Style& Annotation::Style::operator=( const Style &other ) { if ( this != &other ) *d = *other.d; return *this; } void Annotation::Style::setColor( const QColor &color ) { d->m_color = color; } QColor Annotation::Style::color() const { return d->m_color; } void Annotation::Style::setOpacity( double opacity ) { d->m_opacity = opacity; } double Annotation::Style::opacity() const { return d->m_opacity; } void Annotation::Style::setWidth( double width ) { d->m_width = width; } double Annotation::Style::width() const { return d->m_width; } void Annotation::Style::setLineStyle( LineStyle style ) { d->m_style = style; } Annotation::LineStyle Annotation::Style::lineStyle() const { return d->m_style; } void Annotation::Style::setXCorners( double xCorners ) { d->m_xCorners = xCorners; } double Annotation::Style::xCorners() const { return d->m_xCorners; } void Annotation::Style::setYCorners( double yCorners ) { d->m_yCorners = yCorners; } double Annotation::Style::yCorners() const { return d->m_yCorners; } void Annotation::Style::setMarks( int marks ) { d->m_marks = marks; } int Annotation::Style::marks() const { return d->m_marks; } void Annotation::Style::setSpaces( int spaces ) { d->m_spaces = spaces; } int Annotation::Style::spaces() const { return d->m_spaces; } void Annotation::Style::setLineEffect( LineEffect effect ) { d->m_effect = effect; } Annotation::LineEffect Annotation::Style::lineEffect() const { return d->m_effect; } void Annotation::Style::setEffectIntensity( double intensity ) { d->m_effectIntensity = intensity; } double Annotation::Style::effectIntensity() const { return d->m_effectIntensity; } class Annotation::Window::Private { public: Private() : m_flags( -1 ), m_width( 0 ), m_height( 0 ) { } int m_flags; NormalizedPoint m_topLeft; int m_width; int m_height; QString m_title; QString m_summary; }; Annotation::Window::Window() : d( new Private ) { } Annotation::Window::~Window() { delete d; } Annotation::Window::Window( const Window &other ) : d( new Private ) { *d = *other.d; } Annotation::Window& Annotation::Window::operator=( const Window &other ) { if ( this != &other ) *d = *other.d; return *this; } void Annotation::Window::setFlags( int flags ) { d->m_flags = flags; } int Annotation::Window::flags() const { return d->m_flags; } void Annotation::Window::setTopLeft( const NormalizedPoint &point ) { d->m_topLeft = point; } NormalizedPoint Annotation::Window::topLeft() const { return d->m_topLeft; } void Annotation::Window::setWidth( int width ) { d->m_width = width; } int Annotation::Window::width() const { return d->m_width; } void Annotation::Window::setHeight( int height ) { d->m_height = height; } int Annotation::Window::height() const { return d->m_height; } void Annotation::Window::setTitle( const QString &title ) { d->m_title = title; } QString Annotation::Window::title() const { return d->m_title; } void Annotation::Window::setSummary( const QString &summary ) { d->m_summary = summary; } QString Annotation::Window::summary() const { return d->m_summary; } class Annotation::Revision::Private { public: Private() : m_annotation( nullptr ), m_scope( Reply ), m_type( None ) { } Annotation *m_annotation; RevisionScope m_scope; RevisionType m_type; }; Annotation::Revision::Revision() : d( new Private ) { } Annotation::Revision::~Revision() { delete d; } Annotation::Revision::Revision( const Revision &other ) : d( new Private ) { *d = *other.d; } Annotation::Revision& Annotation::Revision::operator=( const Revision &other ) { if ( this != &other ) *d = *other.d; return *this; } void Annotation::Revision::setAnnotation( Annotation *annotation ) { d->m_annotation = annotation; } Annotation *Annotation::Revision::annotation() const { return d->m_annotation; } void Annotation::Revision::setScope( RevisionScope scope ) { d->m_scope = scope; } Annotation::RevisionScope Annotation::Revision::scope() const { return d->m_scope; } void Annotation::Revision::setType( RevisionType type ) { d->m_type = type; } Annotation::RevisionType Annotation::Revision::type() const { return d->m_type; } AnnotationPrivate::AnnotationPrivate() : m_page( nullptr ), m_flags( 0 ), m_disposeFunc( nullptr ) { } AnnotationPrivate::~AnnotationPrivate() { // delete all children revisions if ( m_revisions.isEmpty() ) return; QLinkedList< Annotation::Revision >::iterator it = m_revisions.begin(), end = m_revisions.end(); for ( ; it != end; ++it ) delete (*it).annotation(); } Annotation::Annotation( AnnotationPrivate &dd ) : d_ptr( &dd ) { } Annotation::Annotation( AnnotationPrivate &dd, const QDomNode & annNode ) : d_ptr( &dd ) { d_ptr->setAnnotationProperties( annNode ); } Annotation::~Annotation() { if ( d_ptr->m_disposeFunc ) d_ptr->m_disposeFunc( this ); delete d_ptr; } void Annotation::setAuthor( const QString &author ) { Q_D( Annotation ); d->m_author = author; } QString Annotation::author() const { Q_D( const Annotation ); return d->m_author; } void Annotation::setContents( const QString &contents ) { Q_D( Annotation ); d->m_contents = contents; } QString Annotation::contents() const { Q_D( const Annotation ); return d->m_contents; } void Annotation::setUniqueName( const QString &name ) { Q_D( Annotation ); d->m_uniqueName = name; } QString Annotation::uniqueName() const { Q_D( const Annotation ); return d->m_uniqueName; } void Annotation::setModificationDate( const QDateTime &date ) { Q_D( Annotation ); d->m_modifyDate = date; } QDateTime Annotation::modificationDate() const { Q_D( const Annotation ); return d->m_modifyDate; } void Annotation::setCreationDate( const QDateTime &date ) { Q_D( Annotation ); d->m_creationDate = date; } QDateTime Annotation::creationDate() const { Q_D( const Annotation ); return d->m_creationDate; } void Annotation::setFlags( int flags ) { Q_D( Annotation ); d->m_flags = flags; } int Annotation::flags() const { Q_D( const Annotation ); return d->m_flags; } void Annotation::setBoundingRectangle( const NormalizedRect &rectangle ) { Q_D( Annotation ); d->m_boundary = rectangle; d->resetTransformation(); if ( d->m_page ) { d->transform( d->m_page->rotationMatrix() ); } } NormalizedRect Annotation::boundingRectangle() const { Q_D( const Annotation ); return d->m_boundary; } NormalizedRect Annotation::transformedBoundingRectangle() const { Q_D( const Annotation ); return d->m_transformedBoundary; } void Annotation::translate( const NormalizedPoint &coord ) { Q_D( Annotation ); d->translate( coord ); d->resetTransformation(); if ( d->m_page ) { d->transform( d->m_page->rotationMatrix() ); } } void Annotation::adjust( const NormalizedPoint & deltaCoord1, const NormalizedPoint & deltaCoord2 ) { Q_D( Annotation ); d->adjust( deltaCoord1, deltaCoord2 ); d->resetTransformation(); if ( d->m_page ) { d->transform( d->m_page->rotationMatrix() ); } } bool Annotation::openDialogAfterCreation() const { Q_D( const Annotation ); return d->openDialogAfterCreation(); } Annotation::Style & Annotation::style() { Q_D( Annotation ); return d->m_style; } const Annotation::Style & Annotation::style() const { Q_D( const Annotation ); return d->m_style; } Annotation::Window & Annotation::window() { Q_D( Annotation ); return d->m_window; } const Annotation::Window & Annotation::window() const { Q_D( const Annotation ); return d->m_window; } QLinkedList< Annotation::Revision > & Annotation::revisions() { Q_D( Annotation ); return d->m_revisions; } const QLinkedList< Annotation::Revision > & Annotation::revisions() const { Q_D( const Annotation ); return d->m_revisions; } void Annotation::setNativeId( const QVariant &id ) { Q_D( Annotation ); d->m_nativeId = id; } QVariant Annotation::nativeId() const { Q_D( const Annotation ); return d->m_nativeId; } void Annotation::setDisposeDataFunction( DisposeDataFunction func ) { Q_D( Annotation ); d->m_disposeFunc = func; } bool Annotation::canBeMoved() const { Q_D( const Annotation ); // Don't move annotations if they cannot be modified if ( !d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this) ) return false; // highlight "requires" to be "bounded" to text, and that's tricky for now if ( subType() == AHighlight ) return false; return true; } bool Annotation::canBeResized() const { Q_D( const Annotation ); // Don't resize annotations if they cannot be modified if ( !d->m_page || !d->m_page->m_doc->m_parent->canModifyPageAnnotation(this) ) return false; return d->canBeResized(); } void Annotation::store( QDomNode & annNode, QDomDocument & document ) const { Q_D( const Annotation ); // create [base] element of the annotation node QDomElement e = document.createElement( QStringLiteral("base") ); annNode.appendChild( e ); // store -contents- attributes if ( !d->m_author.isEmpty() ) e.setAttribute( QStringLiteral("author"), d->m_author ); if ( !d->m_contents.isEmpty() ) e.setAttribute( QStringLiteral("contents"), d->m_contents ); if ( !d->m_uniqueName.isEmpty() ) e.setAttribute( QStringLiteral("uniqueName"), d->m_uniqueName ); if ( d->m_modifyDate.isValid() ) e.setAttribute( QStringLiteral("modifyDate"), d->m_modifyDate.toString(Qt::ISODate) ); if ( d->m_creationDate.isValid() ) e.setAttribute( QStringLiteral("creationDate"), d->m_creationDate.toString(Qt::ISODate) ); // store -other- attributes if ( d->m_flags ) // Strip internal flags e.setAttribute( QStringLiteral("flags"), d->m_flags & ~(External | ExternallyDrawn | BeingMoved | BeingResized ) ); if ( d->m_style.color().isValid() ) - e.setAttribute( QStringLiteral("color"), d->m_style.color().name() ); + e.setAttribute( QStringLiteral("color"), d->m_style.color().name( QColor::HexArgb ) ); if ( d->m_style.opacity() != 1.0 ) e.setAttribute( QStringLiteral("opacity"), QString::number( d->m_style.opacity() ) ); // Sub-Node-1 - boundary QDomElement bE = document.createElement( QStringLiteral("boundary") ); e.appendChild( bE ); bE.setAttribute( QStringLiteral("l"), QString::number( d->m_boundary.left ) ); bE.setAttribute( QStringLiteral("t"), QString::number( d->m_boundary.top ) ); bE.setAttribute( QStringLiteral("r"), QString::number( d->m_boundary.right ) ); bE.setAttribute( QStringLiteral("b"), QString::number( d->m_boundary.bottom ) ); // Sub-Node-2 - penStyle if ( d->m_style.width() != 1 || d->m_style.lineStyle() != Solid || d->m_style.xCorners() != 0 || d->m_style.yCorners() != 0.0 || d->m_style.marks() != 3 || d->m_style.spaces() != 0 ) { QDomElement psE = document.createElement( QStringLiteral("penStyle") ); e.appendChild( psE ); psE.setAttribute( QStringLiteral("width"), QString::number( d->m_style.width() ) ); psE.setAttribute( QStringLiteral("style"), (int)d->m_style.lineStyle() ); psE.setAttribute( QStringLiteral("xcr"), QString::number( d->m_style.xCorners() ) ); psE.setAttribute( QStringLiteral("ycr"), QString::number( d->m_style.yCorners() ) ); psE.setAttribute( QStringLiteral("marks"), d->m_style.marks() ); psE.setAttribute( QStringLiteral("spaces"), d->m_style.spaces() ); } // Sub-Node-3 - penEffect if ( d->m_style.lineEffect() != NoEffect || d->m_style.effectIntensity() != 1.0 ) { QDomElement peE = document.createElement( QStringLiteral("penEffect") ); e.appendChild( peE ); peE.setAttribute( QStringLiteral("effect"), (int)d->m_style.lineEffect() ); peE.setAttribute( QStringLiteral("intensity"), QString::number( d->m_style.effectIntensity() ) ); } // Sub-Node-4 - window if ( d->m_window.flags() != -1 || !d->m_window.title().isEmpty() || !d->m_window.summary().isEmpty() ) { QDomElement wE = document.createElement( QStringLiteral("window") ); e.appendChild( wE ); wE.setAttribute( QStringLiteral("flags"), d->m_window.flags() ); wE.setAttribute( QStringLiteral("top"), QString::number( d->m_window.topLeft().x ) ); wE.setAttribute( QStringLiteral("left"), QString::number( d->m_window.topLeft().y ) ); wE.setAttribute( QStringLiteral("width"), d->m_window.width() ); wE.setAttribute( QStringLiteral("height"), d->m_window.height() ); wE.setAttribute( QStringLiteral("title"), d->m_window.title() ); wE.setAttribute( QStringLiteral("summary"), d->m_window.summary() ); } // create [revision] element of the annotation node (if any) if ( d->m_revisions.isEmpty() ) return; // add all revisions as children of revisions element QLinkedList< Revision >::const_iterator it = d->m_revisions.begin(), end = d->m_revisions.end(); for ( ; it != end; ++it ) { // create revision element const Revision & revision = *it; QDomElement r = document.createElement( QStringLiteral("revision") ); annNode.appendChild( r ); // set element attributes r.setAttribute( QStringLiteral("revScope"), (int)revision.scope() ); r.setAttribute( QStringLiteral("revType"), (int)revision.type() ); // use revision as the annotation element, so fill it up AnnotationUtils::storeAnnotation( revision.annotation(), r, document ); } } QDomNode Annotation::getAnnotationPropertiesDomNode() const { QDomDocument doc( QStringLiteral("documentInfo") ); QDomElement node = doc.createElement( QStringLiteral("annotation") ); store(node, doc); return node; } void Annotation::setAnnotationProperties( const QDomNode& node ) { // Save off internal properties that aren't contained in node Okular::PagePrivate *p = d_ptr->m_page; QVariant nativeID = d_ptr->m_nativeId; const int internalFlags = d_ptr->m_flags & (External | ExternallyDrawn | BeingMoved | BeingResized ); Annotation::DisposeDataFunction disposeFunc = d_ptr->m_disposeFunc; // Replace AnnotationPrivate object with a fresh copy AnnotationPrivate *new_d_ptr = d_ptr->getNewAnnotationPrivate(); delete( d_ptr ); d_ptr = new_d_ptr; // Set the annotations properties from node d_ptr->setAnnotationProperties(node); // Restore internal properties d_ptr->m_page = p; d_ptr->m_nativeId = nativeID; d_ptr->m_flags = d_ptr->m_flags | internalFlags; d_ptr->m_disposeFunc = disposeFunc; // Transform annotation to current page rotation d_ptr->transform( d_ptr->m_page->rotationMatrix() ); } double AnnotationPrivate::distanceSqr( double x, double y, double xScale, double yScale ) { return m_transformedBoundary.distanceSqr( x, y, xScale, yScale ); } void AnnotationPrivate::annotationTransform( const QTransform &matrix ) { resetTransformation(); transform( matrix ); } void AnnotationPrivate::transform( const QTransform &matrix ) { m_transformedBoundary.transform( matrix ); } void AnnotationPrivate::baseTransform( const QTransform &matrix ) { m_boundary.transform( matrix ); } void AnnotationPrivate::resetTransformation() { m_transformedBoundary = m_boundary; } void AnnotationPrivate::translate( const NormalizedPoint &coord ) { m_boundary.left = m_boundary.left + coord.x; m_boundary.right = m_boundary.right + coord.x; m_boundary.top = m_boundary.top + coord.y; m_boundary.bottom = m_boundary.bottom + coord.y; } void AnnotationPrivate::adjust( const NormalizedPoint &deltaCoord1, const NormalizedPoint &deltaCoord2 ) { m_boundary.left = m_boundary.left + qBound( -m_boundary.left, deltaCoord1.x, m_boundary.right - m_boundary.left ); m_boundary.top = m_boundary.top + qBound( -m_boundary.top, deltaCoord1.y, m_boundary.bottom - m_boundary.top );; m_boundary.right = m_boundary.right + qBound( m_boundary.left - m_boundary.right, deltaCoord2.x, 1. - m_boundary.right ); m_boundary.bottom = m_boundary.bottom + qBound( m_boundary.top - m_boundary.bottom, deltaCoord2.y, 1. - m_boundary.bottom ); } bool AnnotationPrivate::openDialogAfterCreation() const { return false; } void AnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { // get the [base] element of the annotation node QDomElement e = AnnotationUtils::findChildElement( node, QStringLiteral("base") ); if ( e.isNull() ) return; // parse -contents- attributes if ( e.hasAttribute( QStringLiteral("author") ) ) m_author = e.attribute( QStringLiteral("author") ); if ( e.hasAttribute( QStringLiteral("contents") ) ) m_contents = e.attribute( QStringLiteral("contents") ); if ( e.hasAttribute( QStringLiteral("uniqueName") ) ) m_uniqueName = e.attribute( QStringLiteral("uniqueName") ); if ( e.hasAttribute( QStringLiteral("modifyDate") ) ) m_modifyDate = QDateTime::fromString( e.attribute(QStringLiteral("modifyDate")), Qt::ISODate ); if ( e.hasAttribute( QStringLiteral("creationDate") ) ) m_creationDate = QDateTime::fromString( e.attribute(QStringLiteral("creationDate")), Qt::ISODate ); // parse -other- attributes if ( e.hasAttribute( QStringLiteral("flags") ) ) m_flags = e.attribute( QStringLiteral("flags") ).toInt(); if ( e.hasAttribute( QStringLiteral("color") ) ) m_style.setColor( QColor( e.attribute( QStringLiteral("color") ) ) ); if ( e.hasAttribute( QStringLiteral("opacity") ) ) m_style.setOpacity( e.attribute( QStringLiteral("opacity") ).toDouble() ); // parse -the-subnodes- (describing Style, Window, Revision(s) structures) // Note: all subnodes if present must be 'attributes complete' QDomNode eSubNode = e.firstChild(); while ( eSubNode.isElement() ) { QDomElement ee = eSubNode.toElement(); eSubNode = eSubNode.nextSibling(); // parse boundary if ( ee.tagName() == QLatin1String("boundary") ) { m_boundary=NormalizedRect(ee.attribute( QStringLiteral("l") ).toDouble(), ee.attribute( QStringLiteral("t") ).toDouble(), ee.attribute( QStringLiteral("r") ).toDouble(), ee.attribute( QStringLiteral("b") ).toDouble()); } // parse penStyle if not default else if ( ee.tagName() == QLatin1String("penStyle") ) { m_style.setWidth( ee.attribute( QStringLiteral("width") ).toDouble() ); m_style.setLineStyle( (Annotation::LineStyle)ee.attribute( QStringLiteral("style") ).toInt() ); m_style.setXCorners( ee.attribute( QStringLiteral("xcr") ).toDouble() ); m_style.setYCorners( ee.attribute( QStringLiteral("ycr") ).toDouble() ); m_style.setMarks( ee.attribute( QStringLiteral("marks") ).toInt() ); m_style.setSpaces( ee.attribute( QStringLiteral("spaces") ).toInt() ); } // parse effectStyle if not default else if ( ee.tagName() == QLatin1String("penEffect") ) { m_style.setLineEffect( (Annotation::LineEffect)ee.attribute( QStringLiteral("effect") ).toInt() ); m_style.setEffectIntensity( ee.attribute( QStringLiteral("intensity") ).toDouble() ); } // parse window if present else if ( ee.tagName() == QLatin1String("window") ) { m_window.setFlags( ee.attribute( QStringLiteral("flags") ).toInt() ); m_window.setTopLeft( NormalizedPoint( ee.attribute( QStringLiteral("top") ).toDouble(), ee.attribute( QStringLiteral("left") ).toDouble() ) ); m_window.setWidth( ee.attribute( QStringLiteral("width") ).toInt() ); m_window.setHeight( ee.attribute( QStringLiteral("height") ).toInt() ); m_window.setTitle( ee.attribute( QStringLiteral("title") ) ); m_window.setSummary( ee.attribute( QStringLiteral("summary") ) ); } } // get the [revisions] element of the annotation node QDomNode revNode = node.firstChild(); for ( ; revNode.isElement(); revNode = revNode.nextSibling() ) { QDomElement revElement = revNode.toElement(); if ( revElement.tagName() != QLatin1String("revision") ) continue; // compile the Revision structure crating annotation Annotation::Revision revision; revision.setScope( (Annotation::RevisionScope)revElement.attribute( QStringLiteral("revScope") ).toInt() ); revision.setType( (Annotation::RevisionType)revElement.attribute( QStringLiteral("revType") ).toInt() ); revision.setAnnotation( AnnotationUtils::createAnnotation( revElement ) ); // if annotation is valid, add revision to internal list if ( revision.annotation() ) m_revisions.append( revision ); } m_transformedBoundary = m_boundary; } bool AnnotationPrivate::canBeResized() const { return false; } //END Annotation implementation /** TextAnnotation [Annotation] */ class Okular::TextAnnotationPrivate : public Okular::AnnotationPrivate { public: TextAnnotationPrivate() : AnnotationPrivate(), m_textType( TextAnnotation::Linked ), m_textIcon( QStringLiteral("Comment") ), m_inplaceAlign( 0 ), m_inplaceIntent( TextAnnotation::Unknown ) { } void transform( const QTransform &matrix ) override; void baseTransform( const QTransform &matrix ) override; void resetTransformation() override; void translate( const NormalizedPoint &coord ) override; bool openDialogAfterCreation() const override; void setAnnotationProperties( const QDomNode& node ) override; bool canBeResized() const override; AnnotationPrivate* getNewAnnotationPrivate() override; TextAnnotation::TextType m_textType; QString m_textIcon; QFont m_textFont; int m_inplaceAlign; NormalizedPoint m_inplaceCallout[3]; NormalizedPoint m_transformedInplaceCallout[3]; TextAnnotation::InplaceIntent m_inplaceIntent; }; /* The default textIcon for text annotation is Note as the PDF Reference says */ TextAnnotation::TextAnnotation() : Annotation( *new TextAnnotationPrivate() ) { } TextAnnotation::TextAnnotation( const QDomNode & node ) : Annotation( *new TextAnnotationPrivate(), node ) { } TextAnnotation::~TextAnnotation() { } void TextAnnotation::setTextType( TextType textType ) { Q_D( TextAnnotation ); d->m_textType = textType; } TextAnnotation::TextType TextAnnotation::textType() const { Q_D( const TextAnnotation ); return d->m_textType; } void TextAnnotation::setTextIcon( const QString &icon ) { Q_D( TextAnnotation ); d->m_textIcon = icon; } QString TextAnnotation::textIcon() const { Q_D( const TextAnnotation ); return d->m_textIcon; } void TextAnnotation::setTextFont( const QFont &font ) { Q_D( TextAnnotation ); d->m_textFont = font; } QFont TextAnnotation::textFont() const { Q_D( const TextAnnotation ); return d->m_textFont; } void TextAnnotation::setInplaceAlignment( int alignment ) { Q_D( TextAnnotation ); d->m_inplaceAlign = alignment; } int TextAnnotation::inplaceAlignment() const { Q_D( const TextAnnotation ); return d->m_inplaceAlign; } void TextAnnotation::setInplaceCallout( const NormalizedPoint &point, int index ) { if ( index < 0 || index > 2 ) return; Q_D( TextAnnotation ); d->m_inplaceCallout[ index ] = point; } NormalizedPoint TextAnnotation::inplaceCallout( int index ) const { if ( index < 0 || index > 2 ) return NormalizedPoint(); Q_D( const TextAnnotation ); return d->m_inplaceCallout[ index ]; } NormalizedPoint TextAnnotation::transformedInplaceCallout( int index ) const { if ( index < 0 || index > 2 ) return NormalizedPoint(); Q_D( const TextAnnotation ); return d->m_transformedInplaceCallout[ index ]; } void TextAnnotation::setInplaceIntent( InplaceIntent intent ) { Q_D( TextAnnotation ); d->m_inplaceIntent = intent; } TextAnnotation::InplaceIntent TextAnnotation::inplaceIntent() const { Q_D( const TextAnnotation ); return d->m_inplaceIntent; } Annotation::SubType TextAnnotation::subType() const { return AText; } void TextAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const TextAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [text] element QDomElement textElement = document.createElement( QStringLiteral("text") ); node.appendChild( textElement ); // store the optional attributes if ( d->m_textType != Linked ) textElement.setAttribute( QStringLiteral("type"), (int)d->m_textType ); if ( !d->m_textIcon.isEmpty() ) textElement.setAttribute( QStringLiteral("icon"), d->m_textIcon ); if ( d->m_textFont != QApplication::font() ) textElement.setAttribute( QStringLiteral("font"), d->m_textFont.toString() ); if ( d->m_inplaceAlign ) textElement.setAttribute( QStringLiteral("align"), d->m_inplaceAlign ); if ( d->m_inplaceIntent != Unknown ) textElement.setAttribute( QStringLiteral("intent"), (int)d->m_inplaceIntent ); // Sub-Node - callout if ( d->m_inplaceCallout[0].x != 0.0 ) { QDomElement calloutElement = document.createElement( QStringLiteral("callout") ); textElement.appendChild( calloutElement ); calloutElement.setAttribute( QStringLiteral("ax"), QString::number( d->m_inplaceCallout[0].x ) ); calloutElement.setAttribute( QStringLiteral("ay"), QString::number( d->m_inplaceCallout[0].y ) ); calloutElement.setAttribute( QStringLiteral("bx"), QString::number( d->m_inplaceCallout[1].x ) ); calloutElement.setAttribute( QStringLiteral("by"), QString::number( d->m_inplaceCallout[1].y ) ); calloutElement.setAttribute( QStringLiteral("cx"), QString::number( d->m_inplaceCallout[2].x ) ); calloutElement.setAttribute( QStringLiteral("cy"), QString::number( d->m_inplaceCallout[2].y ) ); } } void TextAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); for ( int i = 0; i < 3; ++i ) { m_transformedInplaceCallout[i].transform( matrix ); } } void TextAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); for ( int i = 0; i < 3; ++i ) { m_inplaceCallout[i].transform( matrix ); } } void TextAnnotationPrivate::resetTransformation() { AnnotationPrivate::resetTransformation(); for ( int i = 0; i < 3; ++i ) { m_transformedInplaceCallout[i] = m_inplaceCallout[i]; } } void TextAnnotationPrivate::translate( const NormalizedPoint &coord ) { AnnotationPrivate::translate( coord ); #define ADD_COORD( c1, c2 ) \ { \ c1.x = c1.x + c2.x; \ c1.y = c1.y + c2.y; \ } ADD_COORD( m_inplaceCallout[0], coord ) ADD_COORD( m_inplaceCallout[1], coord ) ADD_COORD( m_inplaceCallout[2], coord ) #undef ADD_COORD } bool TextAnnotationPrivate::openDialogAfterCreation() const { return ( m_textType == Okular::TextAnnotation::Linked ); } void TextAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'text' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("text") ) continue; // parse the attributes if ( e.hasAttribute( QStringLiteral("type") ) ) m_textType = (TextAnnotation::TextType)e.attribute( QStringLiteral("type") ).toInt(); if ( e.hasAttribute( QStringLiteral("icon") ) ) m_textIcon = e.attribute( QStringLiteral("icon") ); if ( e.hasAttribute( QStringLiteral("font") ) ) m_textFont.fromString( e.attribute( QStringLiteral("font") ) ); if ( e.hasAttribute( QStringLiteral("align") ) ) m_inplaceAlign = e.attribute( QStringLiteral("align") ).toInt(); if ( e.hasAttribute( QStringLiteral("intent") ) ) m_inplaceIntent = (TextAnnotation::InplaceIntent)e.attribute( QStringLiteral("intent") ).toInt(); // parse the subnodes QDomNode eSubNode = e.firstChild(); while ( eSubNode.isElement() ) { QDomElement ee = eSubNode.toElement(); eSubNode = eSubNode.nextSibling(); if ( ee.tagName() == QLatin1String("escapedText") ) { m_contents = ee.firstChild().toCDATASection().data(); } else if ( ee.tagName() == QLatin1String("callout") ) { m_inplaceCallout[0].x = ee.attribute( QStringLiteral("ax") ).toDouble(); m_inplaceCallout[0].y = ee.attribute( QStringLiteral("ay") ).toDouble(); m_inplaceCallout[1].x = ee.attribute( QStringLiteral("bx") ).toDouble(); m_inplaceCallout[1].y = ee.attribute( QStringLiteral("by") ).toDouble(); m_inplaceCallout[2].x = ee.attribute( QStringLiteral("cx") ).toDouble(); m_inplaceCallout[2].y = ee.attribute( QStringLiteral("cy") ).toDouble(); } } // loading complete break; } for ( int i = 0; i < 3; ++i ) m_transformedInplaceCallout[i] = m_inplaceCallout[i]; } bool TextAnnotationPrivate::canBeResized() const { if ( m_textType != TextAnnotation::Linked ) { return true; } return false; } AnnotationPrivate* TextAnnotationPrivate::getNewAnnotationPrivate() { return new TextAnnotationPrivate(); } /** LineAnnotation [Annotation] */ class Okular::LineAnnotationPrivate : public Okular::AnnotationPrivate { public: LineAnnotationPrivate() : AnnotationPrivate(), m_lineStartStyle( LineAnnotation::None ), m_lineEndStyle( LineAnnotation::None ), m_lineClosed( false ), m_lineShowCaption( false ), m_lineLeadingFwdPt( 0 ), m_lineLeadingBackPt( 0 ), m_lineIntent( LineAnnotation::Unknown ) { } void transform( const QTransform &matrix ) override; void baseTransform( const QTransform &matrix ) override; void resetTransformation() override; void translate( const NormalizedPoint &coord ) override; double distanceSqr( double x, double y, double xScale, double yScale ) override; void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; QLinkedList m_linePoints; QLinkedList m_transformedLinePoints; LineAnnotation::TermStyle m_lineStartStyle; LineAnnotation::TermStyle m_lineEndStyle; bool m_lineClosed : 1; bool m_lineShowCaption : 1; QColor m_lineInnerColor; double m_lineLeadingFwdPt; double m_lineLeadingBackPt; LineAnnotation::LineIntent m_lineIntent; }; LineAnnotation::LineAnnotation() : Annotation( *new LineAnnotationPrivate() ) { } LineAnnotation::LineAnnotation( const QDomNode & node ) : Annotation( *new LineAnnotationPrivate(), node ) { } LineAnnotation::~LineAnnotation() { } void LineAnnotation::setLinePoints( const QLinkedList &points ) { Q_D( LineAnnotation ); d->m_linePoints = points; } QLinkedList LineAnnotation::linePoints() const { Q_D( const LineAnnotation ); return d->m_linePoints; } QLinkedList LineAnnotation::transformedLinePoints() const { Q_D( const LineAnnotation ); return d->m_transformedLinePoints; } void LineAnnotation::setLineStartStyle( TermStyle style ) { Q_D( LineAnnotation ); d->m_lineStartStyle = style; } LineAnnotation::TermStyle LineAnnotation::lineStartStyle() const { Q_D( const LineAnnotation ); return d->m_lineStartStyle; } void LineAnnotation::setLineEndStyle( TermStyle style ) { Q_D( LineAnnotation ); d->m_lineEndStyle = style; } LineAnnotation::TermStyle LineAnnotation::lineEndStyle() const { Q_D( const LineAnnotation ); return d->m_lineEndStyle; } void LineAnnotation::setLineClosed( bool closed ) { Q_D( LineAnnotation ); d->m_lineClosed = closed; } bool LineAnnotation::lineClosed() const { Q_D( const LineAnnotation ); return d->m_lineClosed; } void LineAnnotation::setLineInnerColor( const QColor &color ) { Q_D( LineAnnotation ); d->m_lineInnerColor = color; } QColor LineAnnotation::lineInnerColor() const { Q_D( const LineAnnotation ); return d->m_lineInnerColor; } void LineAnnotation::setLineLeadingForwardPoint( double point ) { Q_D( LineAnnotation ); d->m_lineLeadingFwdPt = point; } double LineAnnotation::lineLeadingForwardPoint() const { Q_D( const LineAnnotation ); return d->m_lineLeadingFwdPt; } void LineAnnotation::setLineLeadingBackwardPoint( double point ) { Q_D( LineAnnotation ); d->m_lineLeadingBackPt = point; } double LineAnnotation::lineLeadingBackwardPoint() const { Q_D( const LineAnnotation ); return d->m_lineLeadingBackPt; } void LineAnnotation::setShowCaption( bool show ) { Q_D( LineAnnotation ); d->m_lineShowCaption = show; } bool LineAnnotation::showCaption() const { Q_D( const LineAnnotation ); return d->m_lineShowCaption; } void LineAnnotation::setLineIntent( LineIntent intent ) { Q_D( LineAnnotation ); d->m_lineIntent = intent; } LineAnnotation::LineIntent LineAnnotation::lineIntent() const { Q_D( const LineAnnotation ); return d->m_lineIntent; } Annotation::SubType LineAnnotation::subType() const { return ALine; } void LineAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const LineAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [line] element QDomElement lineElement = document.createElement( QStringLiteral("line") ); node.appendChild( lineElement ); // store the attributes if ( d->m_lineStartStyle != None ) lineElement.setAttribute( QStringLiteral("startStyle"), (int)d->m_lineStartStyle ); if ( d->m_lineEndStyle != None ) lineElement.setAttribute( QStringLiteral("endStyle"), (int)d->m_lineEndStyle ); if ( d->m_lineClosed ) lineElement.setAttribute( QStringLiteral("closed"), d->m_lineClosed ); if ( d->m_lineInnerColor.isValid() ) lineElement.setAttribute( QStringLiteral("innerColor"), d->m_lineInnerColor.name() ); if ( d->m_lineLeadingFwdPt != 0.0 ) lineElement.setAttribute( QStringLiteral("leadFwd"), QString::number( d->m_lineLeadingFwdPt ) ); if ( d->m_lineLeadingBackPt != 0.0 ) lineElement.setAttribute( QStringLiteral("leadBack"), QString::number( d->m_lineLeadingBackPt ) ); if ( d->m_lineShowCaption ) lineElement.setAttribute( QStringLiteral("showCaption"), d->m_lineShowCaption ); if ( d->m_lineIntent != Unknown ) lineElement.setAttribute( QStringLiteral("intent"), d->m_lineIntent ); // append the list of points int points = d->m_linePoints.count(); if ( points > 1 ) { QLinkedList::const_iterator it = d->m_linePoints.begin(), end = d->m_linePoints.end(); while ( it != end ) { const NormalizedPoint & p = *it; QDomElement pElement = document.createElement( QStringLiteral("point") ); lineElement.appendChild( pElement ); pElement.setAttribute( QStringLiteral("x"), QString::number( p.x ) ); pElement.setAttribute( QStringLiteral("y"), QString::number( p.y ) ); it++; //to avoid loop } } } void LineAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); QMutableLinkedListIterator it( m_transformedLinePoints ); while ( it.hasNext() ) it.next().transform( matrix ); } void LineAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); QMutableLinkedListIterator it( m_linePoints ); while ( it.hasNext() ) it.next().transform( matrix ); } void LineAnnotationPrivate::resetTransformation() { AnnotationPrivate::resetTransformation(); m_transformedLinePoints = m_linePoints; } void LineAnnotationPrivate::translate( const NormalizedPoint &coord ) { AnnotationPrivate::translate( coord ); QMutableLinkedListIterator it( m_linePoints ); while ( it.hasNext() ) { NormalizedPoint& p = it.next(); p.x = p.x + coord.x; p.y = p.y + coord.y; } } void LineAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'line' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("line") ) continue; // parse the attributes if ( e.hasAttribute( QStringLiteral("startStyle") ) ) m_lineStartStyle = (LineAnnotation::TermStyle)e.attribute( QStringLiteral("startStyle") ).toInt(); if ( e.hasAttribute( QStringLiteral("endStyle") ) ) m_lineEndStyle = (LineAnnotation::TermStyle)e.attribute( QStringLiteral("endStyle") ).toInt(); if ( e.hasAttribute( QStringLiteral("closed") ) ) m_lineClosed = e.attribute( QStringLiteral("closed") ).toInt(); if ( e.hasAttribute( QStringLiteral("innerColor") ) ) m_lineInnerColor = QColor( e.attribute( QStringLiteral("innerColor") ) ); if ( e.hasAttribute( QStringLiteral("leadFwd") ) ) m_lineLeadingFwdPt = e.attribute( QStringLiteral("leadFwd") ).toDouble(); if ( e.hasAttribute( QStringLiteral("leadBack") ) ) m_lineLeadingBackPt = e.attribute( QStringLiteral("leadBack") ).toDouble(); if ( e.hasAttribute( QStringLiteral("showCaption") ) ) m_lineShowCaption = e.attribute( QStringLiteral("showCaption") ).toInt(); if ( e.hasAttribute( QStringLiteral("intent") ) ) m_lineIntent = (LineAnnotation::LineIntent)e.attribute( QStringLiteral("intent") ).toInt(); // parse all 'point' subnodes QDomNode pointNode = e.firstChild(); while ( pointNode.isElement() ) { QDomElement pe = pointNode.toElement(); pointNode = pointNode.nextSibling(); if ( pe.tagName() != QLatin1String("point") ) continue; NormalizedPoint p; p.x = pe.attribute( QStringLiteral("x"), QStringLiteral("0.0") ).toDouble(); p.y = pe.attribute( QStringLiteral("y"), QStringLiteral("0.0") ).toDouble(); m_linePoints.append( p ); } // loading complete break; } m_transformedLinePoints = m_linePoints; } AnnotationPrivate* LineAnnotationPrivate::getNewAnnotationPrivate() { return new LineAnnotationPrivate(); } double LineAnnotationPrivate::distanceSqr( double x, double y, double xScale, double yScale ) { QLinkedList transformedLinePoints = m_transformedLinePoints; if ( m_lineClosed ) // Close the path transformedLinePoints.append( transformedLinePoints.first() ); if ( m_lineInnerColor.isValid() ) { QPolygonF polygon; foreach ( const NormalizedPoint &p, transformedLinePoints ) polygon.append( QPointF( p.x, p.y ) ); if ( polygon.containsPoint( QPointF( x, y ), Qt::WindingFill ) ) return 0; } return strokeDistance( ::distanceSqr( x, y, xScale, yScale, transformedLinePoints ), m_style.width() * xScale / ( m_page->m_width * 2 ) ); } /** GeomAnnotation [Annotation] */ class Okular::GeomAnnotationPrivate : public Okular::AnnotationPrivate { public: GeomAnnotationPrivate() : AnnotationPrivate(), m_geomType( GeomAnnotation::InscribedSquare ) { } void setAnnotationProperties( const QDomNode& node ) override; bool canBeResized() const override; AnnotationPrivate* getNewAnnotationPrivate() override; double distanceSqr( double x, double y, double xScale, double yScale ) override; GeomAnnotation::GeomType m_geomType; QColor m_geomInnerColor; }; GeomAnnotation::GeomAnnotation() : Annotation( *new GeomAnnotationPrivate() ) { } GeomAnnotation::GeomAnnotation( const QDomNode & node ) : Annotation( *new GeomAnnotationPrivate(), node ) { } GeomAnnotation::~GeomAnnotation() { } void GeomAnnotation::setGeometricalType( GeomType type ) { Q_D( GeomAnnotation ); d->m_geomType = type; } GeomAnnotation::GeomType GeomAnnotation::geometricalType() const { Q_D( const GeomAnnotation ); return d->m_geomType; } void GeomAnnotation::setGeometricalInnerColor( const QColor &color ) { Q_D( GeomAnnotation ); d->m_geomInnerColor = color; } QColor GeomAnnotation::geometricalInnerColor() const { Q_D( const GeomAnnotation ); return d->m_geomInnerColor; } Annotation::SubType GeomAnnotation::subType() const { return AGeom; } void GeomAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const GeomAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [geom] element QDomElement geomElement = document.createElement( QStringLiteral("geom") ); node.appendChild( geomElement ); // append the optional attributes if ( d->m_geomType != InscribedSquare ) geomElement.setAttribute( QStringLiteral("type"), (int)d->m_geomType ); if ( d->m_geomInnerColor.isValid() ) geomElement.setAttribute( QStringLiteral("color"), d->m_geomInnerColor.name() ); } void GeomAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'geom' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("geom") ) continue; // parse the attributes if ( e.hasAttribute( QStringLiteral("type") ) ) m_geomType = (GeomAnnotation::GeomType)e.attribute( QStringLiteral("type") ).toInt(); if ( e.hasAttribute( QStringLiteral("color") ) ) m_geomInnerColor = QColor( e.attribute( QStringLiteral("color") ) ); // compatibility if ( e.hasAttribute( QStringLiteral("width") ) ) m_style.setWidth( e.attribute( QStringLiteral("width") ).toInt() ); // loading complete break; } } bool GeomAnnotationPrivate::canBeResized() const { return true; } AnnotationPrivate* GeomAnnotationPrivate::getNewAnnotationPrivate() { return new GeomAnnotationPrivate(); } double GeomAnnotationPrivate::distanceSqr( double x, double y, double xScale, double yScale ) { double distance = 0; //the line thickness is applied unevenly (only on the "inside") - account for this bool withinShape = false; switch (m_geomType) { case GeomAnnotation::InscribedCircle: { //calculate the center point and focus lengths of the ellipse const double centerX = ( m_transformedBoundary.left + m_transformedBoundary.right ) / 2.0; const double centerY = ( m_transformedBoundary.top + m_transformedBoundary.bottom ) / 2.0; const double focusX = ( m_transformedBoundary.right - centerX); const double focusY = ( m_transformedBoundary.bottom - centerY); const double focusXSqr = pow( focusX, 2 ); const double focusYSqr = pow( focusY, 2 ); // to calculate the distance from the ellipse, we will first find the point "projection" // that lies on the ellipse and is closest to the point (x,y) // This point can obviously be written as "center + lambda(inputPoint - center)". // Because the point lies on the ellipse, we know that: // 1 = ((center.x - projection.x)/focusX)^2 + ((center.y - projection.y)/focusY)^2 // After filling in projection.x = center.x + lambda * (inputPoint.x - center.x) // and its y-equivalent, we can solve for lambda: const double lambda = sqrt( focusXSqr * focusYSqr / ( focusYSqr * pow( x - centerX, 2 ) + focusXSqr * pow( y - centerY, 2 ) ) ); // if the ellipse is filled, we treat all points within as "on" it if ( lambda > 1 ) { if ( m_geomInnerColor.isValid() ) return 0; else withinShape = true; } //otherwise we calculate the squared distance from the projected point on the ellipse NormalizedPoint projection( centerX, centerY ); projection.x += lambda * ( x - centerX ); projection.y += lambda * ( y - centerY ); distance = projection.distanceSqr( x, y, xScale, yScale ); break; } case GeomAnnotation::InscribedSquare: //if the square is filled, only check the bounding box if ( m_geomInnerColor.isValid() ) return AnnotationPrivate::distanceSqr( x, y, xScale, yScale ); QLinkedList edges; edges << NormalizedPoint( m_transformedBoundary.left, m_transformedBoundary.top ); edges << NormalizedPoint( m_transformedBoundary.right, m_transformedBoundary.top ); edges << NormalizedPoint( m_transformedBoundary.right, m_transformedBoundary.bottom ); edges << NormalizedPoint( m_transformedBoundary.left, m_transformedBoundary.bottom ); edges << NormalizedPoint( m_transformedBoundary.left, m_transformedBoundary.top ); distance = ::distanceSqr( x, y, xScale, yScale, edges ); if ( m_transformedBoundary.contains( x, y ) ) withinShape = true; break; } if ( withinShape ) distance = strokeDistance( distance, m_style.width() * xScale / m_page->m_width ); return distance; } /** HighlightAnnotation [Annotation] */ class HighlightAnnotation::Quad::Private { public: Private() { } NormalizedPoint m_points[4]; NormalizedPoint m_transformedPoints[4]; bool m_capStart : 1; bool m_capEnd : 1; double m_feather; }; HighlightAnnotation::Quad::Quad() : d( new Private ) { } HighlightAnnotation::Quad::~Quad() { delete d; } HighlightAnnotation::Quad::Quad( const Quad &other ) : d( new Private ) { *d = *other.d; } HighlightAnnotation::Quad& HighlightAnnotation::Quad::operator=( const Quad &other ) { if ( this != &other ) *d = *other.d; return *this; } void HighlightAnnotation::Quad::setPoint( const NormalizedPoint &point, int index ) { if ( index < 0 || index > 3 ) return; d->m_points[ index ] = point; } NormalizedPoint HighlightAnnotation::Quad::point( int index ) const { if ( index < 0 || index > 3 ) return NormalizedPoint(); return d->m_points[ index ]; } NormalizedPoint HighlightAnnotation::Quad::transformedPoint( int index ) const { if ( index < 0 || index > 3 ) return NormalizedPoint(); return d->m_transformedPoints[ index ]; } void HighlightAnnotation::Quad::setCapStart( bool value ) { d->m_capStart = value; } bool HighlightAnnotation::Quad::capStart() const { return d->m_capStart; } void HighlightAnnotation::Quad::setCapEnd( bool value ) { d->m_capEnd = value; } bool HighlightAnnotation::Quad::capEnd() const { return d->m_capEnd; } void HighlightAnnotation::Quad::setFeather( double width ) { d->m_feather = width; } double HighlightAnnotation::Quad::feather() const { return d->m_feather; } void HighlightAnnotation::Quad::transform( const QTransform &matrix ) { for ( int i = 0; i < 4; ++i ) { d->m_transformedPoints[ i ] = d->m_points[ i ]; d->m_transformedPoints[ i ].transform( matrix ); } } class Okular::HighlightAnnotationPrivate : public Okular::AnnotationPrivate { public: HighlightAnnotationPrivate() : AnnotationPrivate(), m_highlightType( HighlightAnnotation::Highlight ) { } void transform( const QTransform &matrix ) override; void baseTransform( const QTransform &matrix ) override; double distanceSqr( double x, double y, double xScale, double yScale ) override; void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; HighlightAnnotation::HighlightType m_highlightType; QList< HighlightAnnotation::Quad > m_highlightQuads; }; HighlightAnnotation::HighlightAnnotation() : Annotation( *new HighlightAnnotationPrivate() ) { } HighlightAnnotation::HighlightAnnotation( const QDomNode & node ) : Annotation( *new HighlightAnnotationPrivate(), node ) { } HighlightAnnotation::~HighlightAnnotation() { } void HighlightAnnotation::setHighlightType( HighlightType type ) { Q_D( HighlightAnnotation ); d->m_highlightType = type; } HighlightAnnotation::HighlightType HighlightAnnotation::highlightType() const { Q_D( const HighlightAnnotation ); return d->m_highlightType; } QList< HighlightAnnotation::Quad > & HighlightAnnotation::highlightQuads() { Q_D( HighlightAnnotation ); return d->m_highlightQuads; } void HighlightAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const HighlightAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [hl] element QDomElement hlElement = document.createElement( QStringLiteral("hl") ); node.appendChild( hlElement ); // append the optional attributes if ( d->m_highlightType != Highlight ) hlElement.setAttribute( QStringLiteral("type"), (int)d->m_highlightType ); if ( d->m_highlightQuads.count() < 1 ) return; // append highlight quads, all children describe quads QList< Quad >::const_iterator it = d->m_highlightQuads.begin(), end = d->m_highlightQuads.end(); for ( ; it != end; ++it ) { QDomElement quadElement = document.createElement( QStringLiteral("quad") ); hlElement.appendChild( quadElement ); const Quad & q = *it; quadElement.setAttribute( QStringLiteral("ax"), QString::number( q.point( 0 ).x ) ); quadElement.setAttribute( QStringLiteral("ay"), QString::number( q.point( 0 ).y ) ); quadElement.setAttribute( QStringLiteral("bx"), QString::number( q.point( 1 ).x ) ); quadElement.setAttribute( QStringLiteral("by"), QString::number( q.point( 1 ).y ) ); quadElement.setAttribute( QStringLiteral("cx"), QString::number( q.point( 2 ).x ) ); quadElement.setAttribute( QStringLiteral("cy"), QString::number( q.point( 2 ).y ) ); quadElement.setAttribute( QStringLiteral("dx"), QString::number( q.point( 3 ).x ) ); quadElement.setAttribute( QStringLiteral("dy"), QString::number( q.point( 3 ).y ) ); if ( q.capStart() ) quadElement.setAttribute( QStringLiteral("start"), 1 ); if ( q.capEnd() ) quadElement.setAttribute( QStringLiteral("end"), 1 ); quadElement.setAttribute( QStringLiteral("feather"), QString::number( q.feather() ) ); } } Annotation::SubType HighlightAnnotation::subType() const { return AHighlight; } void HighlightAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); QMutableListIterator it( m_highlightQuads ); while ( it.hasNext() ) it.next().transform( matrix ); } void HighlightAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); QMutableListIterator it( m_highlightQuads ); while ( it.hasNext() ) it.next().transform( matrix ); } void HighlightAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); m_highlightQuads.clear(); // loop through the whole children looking for a 'hl' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("hl") ) continue; // parse the attributes if ( e.hasAttribute( QStringLiteral("type") ) ) m_highlightType = (HighlightAnnotation::HighlightType)e.attribute( QStringLiteral("type") ).toInt(); // parse all 'quad' subnodes QDomNode quadNode = e.firstChild(); for ( ; quadNode.isElement(); quadNode = quadNode.nextSibling() ) { QDomElement qe = quadNode.toElement(); if ( qe.tagName() != QLatin1String("quad") ) continue; HighlightAnnotation::Quad q; q.setPoint( NormalizedPoint( qe.attribute( QStringLiteral("ax"), QStringLiteral("0.0") ).toDouble(), qe.attribute( QStringLiteral("ay"), QStringLiteral("0.0") ).toDouble() ), 0 ); q.setPoint( NormalizedPoint( qe.attribute( QStringLiteral("bx"), QStringLiteral("0.0") ).toDouble(), qe.attribute( QStringLiteral("by"), QStringLiteral("0.0") ).toDouble() ), 1 ); q.setPoint( NormalizedPoint( qe.attribute( QStringLiteral("cx"), QStringLiteral("0.0") ).toDouble(), qe.attribute( QStringLiteral("cy"), QStringLiteral("0.0") ).toDouble() ), 2 ); q.setPoint( NormalizedPoint( qe.attribute( QStringLiteral("dx"), QStringLiteral("0.0") ).toDouble(), qe.attribute( QStringLiteral("dy"), QStringLiteral("0.0") ).toDouble() ), 3 ); q.setCapStart( qe.hasAttribute( QStringLiteral("start") ) ); q.setCapEnd( qe.hasAttribute( QStringLiteral("end") ) ); q.setFeather( qe.attribute( QStringLiteral("feather"), QStringLiteral("0.1") ).toDouble() ); q.transform( QTransform() ); m_highlightQuads.append( q ); } // loading complete break; } } AnnotationPrivate* HighlightAnnotationPrivate::getNewAnnotationPrivate() { return new HighlightAnnotationPrivate(); } double HighlightAnnotationPrivate::distanceSqr( double x, double y, double xScale, double yScale ) { NormalizedPoint point( x, y ); double outsideDistance = DBL_MAX; foreach ( const HighlightAnnotation::Quad& quad, m_highlightQuads ) { QLinkedList pathPoints; //first, we check if the point is within the area described by the 4 quads //this is the case, if the point is always on one side of each segments delimiting the polygon: pathPoints << quad.transformedPoint( 0 ); int directionVote = 0; for ( int i = 1; i < 5; ++i ) { NormalizedPoint thisPoint = quad.transformedPoint( i % 4 ); directionVote += (isLeftOfVector( pathPoints.back(), thisPoint, point )) ? 1 : -1; pathPoints << thisPoint; } if ( abs( directionVote ) == 4 ) return 0; //if that's not the case, we treat the outline as path and simply determine //the distance from the path to the point const double thisOutsideDistance = ::distanceSqr( x, y, xScale, yScale, pathPoints ); if ( thisOutsideDistance < outsideDistance ) outsideDistance = thisOutsideDistance; } return outsideDistance; } /** StampAnnotation [Annotation] */ class Okular::StampAnnotationPrivate : public Okular::AnnotationPrivate { public: StampAnnotationPrivate() : AnnotationPrivate(), m_stampIconName( QStringLiteral("Draft") ) { } void setAnnotationProperties( const QDomNode& node ) override; bool canBeResized() const override; AnnotationPrivate* getNewAnnotationPrivate() override; QString m_stampIconName; }; StampAnnotation::StampAnnotation() : Annotation( *new StampAnnotationPrivate() ) { } StampAnnotation::StampAnnotation( const QDomNode & node ) : Annotation( *new StampAnnotationPrivate(), node ) { } StampAnnotation::~StampAnnotation() { } void StampAnnotation::setStampIconName( const QString &name ) { Q_D( StampAnnotation ); d->m_stampIconName = name; } QString StampAnnotation::stampIconName() const { Q_D( const StampAnnotation ); return d->m_stampIconName; } Annotation::SubType StampAnnotation::subType() const { return AStamp; } void StampAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const StampAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [stamp] element QDomElement stampElement = document.createElement( QStringLiteral("stamp") ); node.appendChild( stampElement ); // append the optional attributes if ( d->m_stampIconName != QLatin1String("Draft") ) stampElement.setAttribute( QStringLiteral("icon"), d->m_stampIconName ); } void StampAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'stamp' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("stamp") ) continue; // parse the attributes if ( e.hasAttribute( QStringLiteral("icon") ) ) m_stampIconName = e.attribute( QStringLiteral("icon") ); // loading complete break; } } bool StampAnnotationPrivate::canBeResized() const { return true; } AnnotationPrivate* StampAnnotationPrivate::getNewAnnotationPrivate() { return new StampAnnotationPrivate(); } /** InkAnnotation [Annotation] */ class Okular::InkAnnotationPrivate : public Okular::AnnotationPrivate { public: InkAnnotationPrivate() : AnnotationPrivate() { } void transform( const QTransform &matrix ) override; void baseTransform( const QTransform &matrix ) override; void resetTransformation() override; double distanceSqr( double x, double y, double xScale, double yScale ) override; void translate( const NormalizedPoint &coord ) override; void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; QList< QLinkedList > m_inkPaths; QList< QLinkedList > m_transformedInkPaths; }; InkAnnotation::InkAnnotation() : Annotation( *new InkAnnotationPrivate() ) { } InkAnnotation::InkAnnotation( const QDomNode & node ) : Annotation( *new InkAnnotationPrivate(), node ) { } InkAnnotation::~InkAnnotation() { } void InkAnnotation::setInkPaths( const QList< QLinkedList > &paths ) { Q_D( InkAnnotation ); d->m_inkPaths = paths; } QList< QLinkedList > InkAnnotation::inkPaths() const { Q_D( const InkAnnotation ); return d->m_inkPaths; } QList< QLinkedList > InkAnnotation::transformedInkPaths() const { Q_D( const InkAnnotation ); return d->m_transformedInkPaths; } Annotation::SubType InkAnnotation::subType() const { return AInk; } void InkAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const InkAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [ink] element QDomElement inkElement = document.createElement( QStringLiteral("ink") ); node.appendChild( inkElement ); // append the optional attributes if ( d->m_inkPaths.count() < 1 ) return; QList< QLinkedList >::const_iterator pIt = d->m_inkPaths.begin(), pEnd = d->m_inkPaths.end(); for ( ; pIt != pEnd; ++pIt ) { QDomElement pathElement = document.createElement( QStringLiteral("path") ); inkElement.appendChild( pathElement ); const QLinkedList & path = *pIt; QLinkedList::const_iterator iIt = path.begin(), iEnd = path.end(); for ( ; iIt != iEnd; ++iIt ) { const NormalizedPoint & point = *iIt; QDomElement pointElement = document.createElement( QStringLiteral("point") ); pathElement.appendChild( pointElement ); pointElement.setAttribute( QStringLiteral("x"), QString::number( point.x ) ); pointElement.setAttribute( QStringLiteral("y"), QString::number( point.y ) ); } } } double InkAnnotationPrivate::distanceSqr( double x, double y, double xScale, double yScale ) { double distance = DBL_MAX; foreach ( const QLinkedList& path, m_transformedInkPaths ) { const double thisDistance = ::distanceSqr( x, y, xScale, yScale, path ); if ( thisDistance < distance ) distance = thisDistance; } return strokeDistance( distance, m_style.width() * xScale / ( m_page->m_width * 2 ) ); } void InkAnnotationPrivate::transform( const QTransform &matrix ) { AnnotationPrivate::transform( matrix ); for ( int i = 0; i < m_transformedInkPaths.count(); ++i ) { QMutableLinkedListIterator it( m_transformedInkPaths[ i ] ); while ( it.hasNext() ) it.next().transform( matrix ); } } void InkAnnotationPrivate::baseTransform( const QTransform &matrix ) { AnnotationPrivate::baseTransform( matrix ); for ( int i = 0; i < m_inkPaths.count(); ++i ) { QMutableLinkedListIterator it( m_inkPaths[ i ] ); while ( it.hasNext() ) it.next().transform( matrix ); } } void InkAnnotationPrivate::resetTransformation() { AnnotationPrivate::resetTransformation(); m_transformedInkPaths = m_inkPaths; } void InkAnnotationPrivate::translate( const NormalizedPoint &coord ) { AnnotationPrivate::translate( coord ); for ( int i = 0; i < m_inkPaths.count(); ++i ) { QMutableLinkedListIterator it( m_inkPaths[ i ] ); while ( it.hasNext() ) { NormalizedPoint& p = it.next(); p.x = p.x + coord.x; p.y = p.y + coord.y; } } } void InkAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); m_inkPaths.clear(); // loop through the whole children looking for a 'ink' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("ink") ) continue; // parse the 'path' subnodes QDomNode pathNode = e.firstChild(); while ( pathNode.isElement() ) { QDomElement pathElement = pathNode.toElement(); pathNode = pathNode.nextSibling(); if ( pathElement.tagName() != QLatin1String("path") ) continue; // build each path parsing 'point' subnodes QLinkedList path; QDomNode pointNode = pathElement.firstChild(); while ( pointNode.isElement() ) { QDomElement pointElement = pointNode.toElement(); pointNode = pointNode.nextSibling(); if ( pointElement.tagName() != QLatin1String("point") ) continue; NormalizedPoint p; p.x = pointElement.attribute( QStringLiteral("x"), QStringLiteral("0.0") ).toDouble(); p.y = pointElement.attribute( QStringLiteral("y"), QStringLiteral("0.0") ).toDouble(); path.append( p ); } // add the path to the path list if it contains at least 2 nodes if ( path.count() >= 2 ) m_inkPaths.append( path ); } // loading complete break; } m_transformedInkPaths = m_inkPaths; } AnnotationPrivate* InkAnnotationPrivate::getNewAnnotationPrivate() { return new InkAnnotationPrivate(); } /** CaretAnnotation [Annotation] */ class Okular::CaretAnnotationPrivate : public Okular::AnnotationPrivate { public: CaretAnnotationPrivate() : AnnotationPrivate(), m_symbol( CaretAnnotation::None ) { } void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; CaretAnnotation::CaretSymbol m_symbol; }; static QString caretSymbolToString( CaretAnnotation::CaretSymbol symbol ) { switch ( symbol ) { case CaretAnnotation::None: return QStringLiteral( "None" ); case CaretAnnotation::P: return QStringLiteral( "P" ); } return QString(); } static CaretAnnotation::CaretSymbol caretSymbolFromString( const QString &symbol ) { if ( symbol == QLatin1String( "None" ) ) return CaretAnnotation::None; else if ( symbol == QLatin1String( "P" ) ) return CaretAnnotation::P; return CaretAnnotation::None; } void CaretAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'caret' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("caret") ) continue; // parse the attributes if ( e.hasAttribute( QStringLiteral("symbol") ) ) m_symbol = caretSymbolFromString( e.attribute( QStringLiteral("symbol") ) ); // loading complete break; } } AnnotationPrivate* CaretAnnotationPrivate::getNewAnnotationPrivate() { return new CaretAnnotationPrivate(); } CaretAnnotation::CaretAnnotation() : Annotation( *new CaretAnnotationPrivate() ) { } CaretAnnotation::CaretAnnotation( const QDomNode & node ) : Annotation( *new CaretAnnotationPrivate(), node ) { } CaretAnnotation::~CaretAnnotation() { } void CaretAnnotation::setCaretSymbol( CaretAnnotation::CaretSymbol symbol ) { Q_D( CaretAnnotation ); d->m_symbol = symbol; } CaretAnnotation::CaretSymbol CaretAnnotation::caretSymbol() const { Q_D( const CaretAnnotation ); return d->m_symbol; } Annotation::SubType CaretAnnotation::subType() const { return ACaret; } void CaretAnnotation::store( QDomNode & node, QDomDocument & document ) const { Q_D( const CaretAnnotation ); // recurse to parent objects storing properties Annotation::store( node, document ); // create [caret] element QDomElement caretElement = document.createElement( QStringLiteral("caret") ); node.appendChild( caretElement ); // append the optional attributes if ( d->m_symbol != None ) caretElement.setAttribute( QStringLiteral("symbol"), caretSymbolToString( d->m_symbol ) ); } /** FileAttachmentAnnotation [Annotation] */ class Okular::FileAttachmentAnnotationPrivate : public Okular::AnnotationPrivate { public: FileAttachmentAnnotationPrivate() : AnnotationPrivate(), icon( QStringLiteral("PushPin") ), embfile( nullptr ) { } ~FileAttachmentAnnotationPrivate() override { delete embfile; } void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; // data fields QString icon; EmbeddedFile *embfile; }; void FileAttachmentAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'fileattachment' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("fileattachment") ) continue; // loading complete break; } } AnnotationPrivate* FileAttachmentAnnotationPrivate::getNewAnnotationPrivate() { return new FileAttachmentAnnotationPrivate(); } FileAttachmentAnnotation::FileAttachmentAnnotation() : Annotation( *new FileAttachmentAnnotationPrivate() ) { } FileAttachmentAnnotation::FileAttachmentAnnotation( const QDomNode & node ) : Annotation( *new FileAttachmentAnnotationPrivate(), node ) { } FileAttachmentAnnotation::~FileAttachmentAnnotation() { } void FileAttachmentAnnotation::store( QDomNode & node, QDomDocument & document ) const { // recurse to parent objects storing properties Annotation::store( node, document ); // create [fileattachment] element QDomElement fileAttachmentElement = document.createElement( QStringLiteral("fileattachment") ); node.appendChild( fileAttachmentElement ); } Annotation::SubType FileAttachmentAnnotation::subType() const { return AFileAttachment; } QString FileAttachmentAnnotation::fileIconName() const { Q_D( const FileAttachmentAnnotation ); return d->icon; } void FileAttachmentAnnotation::setFileIconName( const QString &icon ) { Q_D( FileAttachmentAnnotation ); d->icon = icon; } EmbeddedFile* FileAttachmentAnnotation::embeddedFile() const { Q_D( const FileAttachmentAnnotation ); return d->embfile; } void FileAttachmentAnnotation::setEmbeddedFile( EmbeddedFile *ef ) { Q_D( FileAttachmentAnnotation ); d->embfile = ef; } /** SoundAnnotation [Annotation] */ class Okular::SoundAnnotationPrivate : public Okular::AnnotationPrivate { public: SoundAnnotationPrivate() : AnnotationPrivate(), icon( QStringLiteral("Speaker") ), sound( nullptr ) { } ~SoundAnnotationPrivate() override { delete sound; } void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; // data fields QString icon; Sound *sound; }; void SoundAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'sound' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("sound") ) continue; // loading complete break; } } AnnotationPrivate* SoundAnnotationPrivate::getNewAnnotationPrivate() { return new SoundAnnotationPrivate(); } SoundAnnotation::SoundAnnotation() : Annotation( *new SoundAnnotationPrivate() ) { } SoundAnnotation::SoundAnnotation( const QDomNode & node ) : Annotation( *new SoundAnnotationPrivate(), node ) { } SoundAnnotation::~SoundAnnotation() { } void SoundAnnotation::store( QDomNode & node, QDomDocument & document ) const { // recurse to parent objects storing properties Annotation::store( node, document ); // create [sound] element QDomElement soundElement = document.createElement( QStringLiteral("sound") ); node.appendChild( soundElement ); } Annotation::SubType SoundAnnotation::subType() const { return ASound; } QString SoundAnnotation::soundIconName() const { Q_D( const SoundAnnotation ); return d->icon; } void SoundAnnotation::setSoundIconName( const QString &icon ) { Q_D( SoundAnnotation ); d->icon = icon; } Sound* SoundAnnotation::sound() const { Q_D( const SoundAnnotation ); return d->sound; } void SoundAnnotation::setSound( Sound *s ) { Q_D( SoundAnnotation ); d->sound = s; } /** MovieAnnotation [Annotation] */ class Okular::MovieAnnotationPrivate : public Okular::AnnotationPrivate { public: MovieAnnotationPrivate() : AnnotationPrivate(), movie( nullptr ) { } ~MovieAnnotationPrivate() override { delete movie; } void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; // data fields Movie *movie; }; void MovieAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'movie' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("movie") ) continue; // loading complete break; } } AnnotationPrivate* MovieAnnotationPrivate::getNewAnnotationPrivate() { return new MovieAnnotationPrivate(); } MovieAnnotation::MovieAnnotation() : Annotation( *new MovieAnnotationPrivate() ) { } MovieAnnotation::MovieAnnotation( const QDomNode & node ) : Annotation( *new MovieAnnotationPrivate(), node ) { } MovieAnnotation::~MovieAnnotation() { } void MovieAnnotation::store( QDomNode & node, QDomDocument & document ) const { // recurse to parent objects storing properties Annotation::store( node, document ); // create [movie] element QDomElement movieElement = document.createElement( QStringLiteral("movie") ); node.appendChild( movieElement ); } Annotation::SubType MovieAnnotation::subType() const { return AMovie; } Movie* MovieAnnotation::movie() const { Q_D( const MovieAnnotation ); return d->movie; } void MovieAnnotation::setMovie( Movie *movie ) { Q_D( MovieAnnotation ); d->movie = movie; } /** ScreenAnnotation [Annotation] */ class Okular::ScreenAnnotationPrivate : public Okular::AnnotationPrivate { public: ScreenAnnotationPrivate(); ~ScreenAnnotationPrivate() override; void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; Okular::Action* m_action; QMap< Okular::Annotation::AdditionalActionType, Okular::Action* > m_additionalActions; }; ScreenAnnotationPrivate::ScreenAnnotationPrivate() : m_action( nullptr ) { } ScreenAnnotationPrivate::~ScreenAnnotationPrivate() { delete m_action; qDeleteAll( m_additionalActions ); } void ScreenAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'screen' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("screen") ) continue; // loading complete break; } } AnnotationPrivate* ScreenAnnotationPrivate::getNewAnnotationPrivate() { return new ScreenAnnotationPrivate(); } ScreenAnnotation::ScreenAnnotation() : Annotation( *new ScreenAnnotationPrivate() ) { } ScreenAnnotation::ScreenAnnotation( const QDomNode & node ) : Annotation( *new ScreenAnnotationPrivate(), node ) { } ScreenAnnotation::~ScreenAnnotation() { } void ScreenAnnotation::store( QDomNode & node, QDomDocument & document ) const { // recurse to parent objects storing properties Annotation::store( node, document ); // create [screen] element QDomElement movieElement = document.createElement( QStringLiteral("screen") ); node.appendChild( movieElement ); } Annotation::SubType ScreenAnnotation::subType() const { return AScreen; } void ScreenAnnotation::setAdditionalAction( AdditionalActionType type, Action *action ) { Q_D( ScreenAnnotation ); if ( d->m_additionalActions.contains( type ) ) delete d->m_additionalActions.value( type ); d->m_additionalActions.insert( type, action ); } Action* ScreenAnnotation::additionalAction( AdditionalActionType type ) const { Q_D( const ScreenAnnotation ); if ( !d->m_additionalActions.contains( type ) ) return nullptr; else return d->m_additionalActions.value( type ); } void ScreenAnnotation::setAction( Action *action ) { Q_D( ScreenAnnotation ); delete d->m_action; d->m_action = action; } Action* ScreenAnnotation::action() const { Q_D( const ScreenAnnotation ); return d->m_action; } /** WidgetAnnotation [Annotation] */ class Okular::WidgetAnnotationPrivate : public Okular::AnnotationPrivate { public: ~WidgetAnnotationPrivate() override; void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; QMap< Okular::Annotation::AdditionalActionType, Okular::Action* > m_additionalActions; }; WidgetAnnotationPrivate::~WidgetAnnotationPrivate() { qDeleteAll( m_additionalActions ); } void WidgetAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'widget' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("widget") ) continue; // loading complete break; } } AnnotationPrivate* WidgetAnnotationPrivate::getNewAnnotationPrivate() { return new WidgetAnnotationPrivate(); } WidgetAnnotation::WidgetAnnotation() : Annotation( *new WidgetAnnotationPrivate() ) { } WidgetAnnotation::WidgetAnnotation( const QDomNode & node ) : Annotation( *new WidgetAnnotationPrivate, node ) { } WidgetAnnotation::~WidgetAnnotation() { } void WidgetAnnotation::store( QDomNode & node, QDomDocument & document ) const { // recurse to parent objects storing properties Annotation::store( node, document ); // create [widget] element QDomElement movieElement = document.createElement( QStringLiteral("widget") ); node.appendChild( movieElement ); } Annotation::SubType WidgetAnnotation::subType() const { return AWidget; } void WidgetAnnotation::setAdditionalAction( AdditionalActionType type, Action *action ) { Q_D( WidgetAnnotation ); if ( d->m_additionalActions.contains( type ) ) delete d->m_additionalActions.value( type ); d->m_additionalActions.insert( type, action ); } Action* WidgetAnnotation::additionalAction( AdditionalActionType type ) const { Q_D( const WidgetAnnotation ); if ( !d->m_additionalActions.contains( type ) ) return nullptr; else return d->m_additionalActions.value( type ); } /** RichMediaAnnotation [Annotation] */ class Okular::RichMediaAnnotationPrivate : public Okular::AnnotationPrivate { public: RichMediaAnnotationPrivate(); ~RichMediaAnnotationPrivate() override; void setAnnotationProperties( const QDomNode& node ) override; AnnotationPrivate* getNewAnnotationPrivate() override; // data fields Movie *movie; EmbeddedFile *embeddedFile; }; RichMediaAnnotationPrivate::RichMediaAnnotationPrivate() : movie( nullptr ), embeddedFile( nullptr ) { } RichMediaAnnotationPrivate::~RichMediaAnnotationPrivate() { delete movie; delete embeddedFile; } void RichMediaAnnotationPrivate::setAnnotationProperties( const QDomNode& node ) { Okular::AnnotationPrivate::setAnnotationProperties(node); // loop through the whole children looking for a 'richMedia' element QDomNode subNode = node.firstChild(); while( subNode.isElement() ) { QDomElement e = subNode.toElement(); subNode = subNode.nextSibling(); if ( e.tagName() != QLatin1String("richMedia") ) continue; // loading complete break; } } AnnotationPrivate* RichMediaAnnotationPrivate::getNewAnnotationPrivate() { return new RichMediaAnnotationPrivate(); } RichMediaAnnotation::RichMediaAnnotation() : Annotation( *new RichMediaAnnotationPrivate() ) { } RichMediaAnnotation::RichMediaAnnotation( const QDomNode & node ) : Annotation( *new RichMediaAnnotationPrivate, node ) { } RichMediaAnnotation::~RichMediaAnnotation() { } void RichMediaAnnotation::store( QDomNode & node, QDomDocument & document ) const { // recurse to parent objects storing properties Annotation::store( node, document ); // create [richMedia] element QDomElement movieElement = document.createElement( QStringLiteral("richMedia") ); node.appendChild( movieElement ); } Annotation::SubType RichMediaAnnotation::subType() const { return ARichMedia; } void RichMediaAnnotation::setMovie( Movie *movie ) { Q_D( RichMediaAnnotation ); delete d->movie; d->movie = movie; } Movie* RichMediaAnnotation::movie() const { Q_D( const RichMediaAnnotation ); return d->movie; } EmbeddedFile* RichMediaAnnotation::embeddedFile() const { Q_D( const RichMediaAnnotation ); return d->embeddedFile; } void RichMediaAnnotation::setEmbeddedFile( EmbeddedFile *embeddedFile ) { Q_D( RichMediaAnnotation ); delete d->embeddedFile; d->embeddedFile = embeddedFile; } diff --git a/ui/data/tools.xml b/ui/data/tools.xml index 1fd590481..2848b2049 100644 --- a/ui/data/tools.xml +++ b/ui/data/tools.xml @@ -1,74 +1,74 @@ - + 1 - + 2 - + 3 - + 4 - + 5 - + 6 7 - + 8 - + 9