diff --git a/conf/editannottooldialog.cpp b/conf/editannottooldialog.cpp index 1b8a9e2c6..a6bec3841 100644 --- a/conf/editannottooldialog.cpp +++ b/conf/editannottooldialog.cpp @@ -1,493 +1,527 @@ #include "editannottooldialog.h" #include #include #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 ) ); + m_type->addItem( i18n("Typewriter"), qVariantFromValue( ToolTypewriter ) ); 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() ); } + else if ( toolType == ToolTypewriter ) + { + Okular::TextAnnotation * ta = static_cast( m_stubann ); + toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("typewriter") ); + engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PickPoint") ); + engineElement.setAttribute( QStringLiteral("color"), QStringLiteral("#000000") ); + engineElement.setAttribute( QStringLiteral("block"), QStringLiteral("true") ); + annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Typewriter") ); + annotationElement.setAttribute( QStringLiteral("color"), color ); + annotationElement.setAttribute( QStringLiteral("width"), width ); + if ( ta->textFont() != QApplication::font() ) + annotationElement.setAttribute( QStringLiteral("font"), ta->textFont().toString() ); + } 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; } + else if ( toolType == ToolTypewriter ) + { + Okular::TextAnnotation * ta = new Okular::TextAnnotation(); + ta->setTextType( Okular::TextAnnotation::InPlace ); + ta->setInplaceIntent( Okular::TextAnnotation::TypeWriter ); + ta->style().setWidth( 0.0 ); + ta->style().setColor( QColor(255,255,255,0) ); + m_stubann = ta; + } } 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 ); } + else if ( annotType == QLatin1String("typewriter") ) + { + setToolType( ToolTypewriter ); + Okular::TextAnnotation * ta = static_cast( m_stubann ); + if ( annotationElement.hasAttribute( QStringLiteral("font") ) ) + { + QFont f; + f.fromString( annotationElement.attribute( QStringLiteral("font") ) ); + ta->setTextFont( f ); + } + } // 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/conf/editannottooldialog.h b/conf/editannottooldialog.h index 23372b17d..28537dda7 100644 --- a/conf/editannottooldialog.h +++ b/conf/editannottooldialog.h @@ -1,73 +1,74 @@ /*************************************************************************** * Copyright (C) 2012 by Fabio D'Urso * * * * 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. * ***************************************************************************/ #ifndef EDITANNOTTOOLDIALOG_H #define EDITANNOTTOOLDIALOG_H #include #include class KLineEdit; class KComboBox; class QLabel; class QListWidget; class QListWidgetItem; class QGroupBox; class AnnotationWidget; namespace Okular { class Annotation; } class EditAnnotToolDialog : public QDialog { Q_OBJECT public: enum ToolType { ToolNoteLinked, ToolNoteInline, ToolInk, ToolStraightLine, ToolPolygon, ToolTextMarkup, ToolGeometricalShape, - ToolStamp + ToolStamp, + ToolTypewriter }; EditAnnotToolDialog( QWidget *parent = nullptr, const QDomElement &initialState = QDomElement() ); ~EditAnnotToolDialog(); QString name() const; QDomDocument toolXml() const; private: void createStubAnnotation(); void rebuildAppearanceBox(); void updateDefaultNameAndIcon(); void setToolType( ToolType newType ); void loadTool( const QDomElement &toolElement ); KLineEdit *m_name; KComboBox *m_type; QLabel *m_toolIcon; QGroupBox *m_appearanceBox; Okular::Annotation *m_stubann; AnnotationWidget *m_annotationWidget; private Q_SLOTS: void slotTypeChanged(); void slotDataChanged(); }; Q_DECLARE_METATYPE( EditAnnotToolDialog::ToolType ) #endif // EDITANNOTTOOLDIALOG_H diff --git a/core/annotations.cpp b/core/annotations.cpp index d9f3fd439..b60754e5a 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/annotationpropertiesdialog.cpp b/ui/annotationpropertiesdialog.cpp index f15032083..a2f6f924d 100644 --- a/ui/annotationpropertiesdialog.cpp +++ b/ui/annotationpropertiesdialog.cpp @@ -1,185 +1,190 @@ /*************************************************************************** * Copyright (C) 2006 by Chu Xiaodong * * * * 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 "annotationpropertiesdialog.h" // qt/kde includes #include #include #include #include #include #include #include #include #include // local includes #include "core/document.h" #include "core/page.h" #include "core/annotations.h" #include "annotationwidgets.h" AnnotsPropertiesDialog::AnnotsPropertiesDialog( QWidget *parent, Okular::Document *document, int docpage, Okular::Annotation *ann ) : KPageDialog( parent ), m_document( document ), m_page( docpage ), modified( false ) { setFaceType( Tabbed ); m_annot=ann; const bool canEditAnnotations = m_document->canModifyPageAnnotation( ann ); setCaptionTextbyAnnotType(); if ( canEditAnnotations ) { setStandardButtons( QDialogButtonBox::Ok | QDialogButtonBox::Apply | QDialogButtonBox::Cancel ); button( QDialogButtonBox::Apply )->setEnabled( false ); connect( button( QDialogButtonBox::Apply ), &QPushButton::clicked, this, &AnnotsPropertiesDialog::slotapply); connect( button( QDialogButtonBox::Ok ), &QPushButton::clicked, this, &AnnotsPropertiesDialog::slotapply); } else { setStandardButtons( QDialogButtonBox::Close ); button( QDialogButtonBox::Close )->setDefault( true ); } m_annotWidget = AnnotationWidgetFactory::widgetFor( ann ); QLabel* tmplabel; //1. Appearance //BEGIN tab1 QWidget *appearanceWidget = m_annotWidget->appearanceWidget(); appearanceWidget->setEnabled( canEditAnnotations ); addPage( appearanceWidget, i18n( "&Appearance" ) ); //END tab1 //BEGIN tab 2 QFrame* page = new QFrame( this ); addPage( page, i18n( "&General" ) ); // m_tabitem[1]->setIcon( QIcon::fromTheme( "fonts" ) ); QGridLayout* gridlayout = new QGridLayout( page ); tmplabel = new QLabel( i18n( "&Author:" ), page ); AuthorEdit = new KLineEdit( ann->author(), page ); AuthorEdit->setEnabled( canEditAnnotations ); tmplabel->setBuddy( AuthorEdit ); gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); gridlayout->addWidget( AuthorEdit, 0, 1 ); tmplabel = new QLabel( page ); tmplabel->setText( i18n( "Created: %1", QLocale().toString( ann->creationDate(), QLocale::LongFormat ) ) ); tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse ); gridlayout->addWidget( tmplabel, 1, 0, 1, 2 ); m_modifyDateLabel = new QLabel( page ); m_modifyDateLabel->setText( i18n( "Modified: %1", QLocale().toString( ann->modificationDate(), QLocale::LongFormat ) ) ); m_modifyDateLabel->setTextInteractionFlags( Qt::TextSelectableByMouse ); gridlayout->addWidget( m_modifyDateLabel, 2, 0, 1, 2 ); gridlayout->addItem( new QSpacerItem( 5, 5, QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ), 3, 0 ); //END tab 2 QWidget * extraWidget = m_annotWidget->extraWidget(); if ( extraWidget ) { addPage( extraWidget, extraWidget->windowTitle() ); } //BEGIN connections connect(AuthorEdit, &QLineEdit::textChanged, this, &AnnotsPropertiesDialog::setModified); connect(m_annotWidget, &AnnotationWidget::dataChanged, this, &AnnotsPropertiesDialog::setModified); //END #if 0 qCDebug(OkularUiDebug) << "Annotation details:"; qCDebug(OkularUiDebug).nospace() << " => unique name: '" << ann->uniqueName() << "'"; qCDebug(OkularUiDebug) << " => flags:" << QString::number( m_annot->flags(), 2 ); #endif resize( sizeHint() ); } AnnotsPropertiesDialog::~AnnotsPropertiesDialog() { delete m_annotWidget; } void AnnotsPropertiesDialog::setCaptionTextbyAnnotType() { Okular::Annotation::SubType type=m_annot->subType(); QString captiontext; switch(type) { case Okular::Annotation::AText: if(((Okular::TextAnnotation*)m_annot)->textType()==Okular::TextAnnotation::Linked) captiontext = i18n( "Pop-up Note Properties" ); else - captiontext = i18n( "Inline Note Properties" ); + { + if(((Okular::TextAnnotation*)m_annot)->inplaceIntent()==Okular::TextAnnotation::TypeWriter) + captiontext = i18n( "Typewriter Properties" ); + else + captiontext = i18n( "Inline Note Properties" ); + } break; case Okular::Annotation::ALine: if ( ((Okular::LineAnnotation*)m_annot)->linePoints().count() == 2 ) captiontext = i18n( "Straight Line Properties" ); else captiontext = i18n( "Polygon Properties" ); break; case Okular::Annotation::AGeom: captiontext = i18n( "Geometry Properties" ); break; case Okular::Annotation::AHighlight: captiontext = i18n( "Text Markup Properties" ); break; case Okular::Annotation::AStamp: captiontext = i18n( "Stamp Properties" ); break; case Okular::Annotation::AInk: captiontext = i18n( "Freehand Line Properties" ); break; case Okular::Annotation::ACaret: captiontext = i18n( "Caret Properties" ); break; case Okular::Annotation::AFileAttachment: captiontext = i18n( "File Attachment Properties" ); break; case Okular::Annotation::ASound: captiontext = i18n( "Sound Properties" ); break; case Okular::Annotation::AMovie: captiontext = i18n( "Movie Properties" ); break; default: captiontext = i18n( "Annotation Properties" ); break; } setWindowTitle( captiontext ); } void AnnotsPropertiesDialog::setModified() { modified = true; button( QDialogButtonBox::Apply )->setEnabled( true ); } void AnnotsPropertiesDialog::slotapply() { if ( !modified ) return; m_document->prepareToModifyAnnotationProperties( m_annot ); m_annot->setAuthor( AuthorEdit->text() ); m_annot->setModificationDate( QDateTime::currentDateTime() ); m_annotWidget->applyChanges(); m_document->modifyPageAnnotationProperties( m_page, m_annot ); m_modifyDateLabel->setText( i18n( "Modified: %1", QLocale().toString( m_annot->modificationDate(), QLocale::LongFormat ) ) ); modified = false; button( QDialogButtonBox::Apply )->setEnabled( false ); } #include "moc_annotationpropertiesdialog.cpp" diff --git a/ui/annotationwidgets.cpp b/ui/annotationwidgets.cpp index 14332918f..9ee3e2cdd 100644 --- a/ui/annotationwidgets.cpp +++ b/ui/annotationwidgets.cpp @@ -1,776 +1,785 @@ /*************************************************************************** * Copyright (C) 2006 by Pino Toscano * * * * 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 "annotationwidgets.h" // qt/kde includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/document.h" #include "guiutils.h" #define FILEATTACH_ICONSIZE 48 PixmapPreviewSelector::PixmapPreviewSelector( QWidget * parent ) : QWidget( parent ) { QHBoxLayout * mainlay = new QHBoxLayout( this ); mainlay->setMargin( 0 ); m_comboItems = new KComboBox( this ); mainlay->addWidget( m_comboItems ); m_iconLabel = new QLabel( this ); mainlay->addWidget( m_iconLabel ); m_iconLabel->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_iconLabel->setAlignment( Qt::AlignCenter ); m_iconLabel->setFrameStyle( QFrame::StyledPanel ); setPreviewSize( 32 ); connect( m_comboItems, SIGNAL(currentIndexChanged(QString)), this, SLOT(iconComboChanged(QString)) ); connect( m_comboItems, &QComboBox::editTextChanged, this, &PixmapPreviewSelector::iconComboChanged ); } PixmapPreviewSelector::~PixmapPreviewSelector() { } void PixmapPreviewSelector::setIcon( const QString& icon ) { int id = m_comboItems->findData( QVariant( icon ), Qt::UserRole, Qt::MatchFixedString ); if ( id == -1 ) id = m_comboItems->findText( icon, Qt::MatchFixedString ); if ( id > -1 ) { m_comboItems->setCurrentIndex( id ); } else if ( m_comboItems->isEditable() ) { m_comboItems->addItem( icon, QVariant( icon ) ); m_comboItems->setCurrentIndex( m_comboItems->findText( icon, Qt::MatchFixedString ) ); } } QString PixmapPreviewSelector::icon() const { return m_icon; } void PixmapPreviewSelector::addItem( const QString& item, const QString& id ) { m_comboItems->addItem( item, QVariant( id ) ); setIcon( m_icon ); } void PixmapPreviewSelector::setPreviewSize( int size ) { m_previewSize = size; m_iconLabel->setFixedSize( m_previewSize + 8, m_previewSize + 8 ); iconComboChanged( m_icon ); } int PixmapPreviewSelector::previewSize() const { return m_previewSize; } void PixmapPreviewSelector::setEditable( bool editable ) { m_comboItems->setEditable( editable ); } void PixmapPreviewSelector::iconComboChanged( const QString& icon ) { int id = m_comboItems->findText( icon, Qt::MatchFixedString ); if ( id >= 0 ) { m_icon = m_comboItems->itemData( id ).toString(); } else { m_icon = icon; } QPixmap pixmap = GuiUtils::loadStamp( m_icon, QSize(), m_previewSize ); const QRect cr = m_iconLabel->contentsRect(); if ( pixmap.width() > cr.width() || pixmap.height() > cr.height() ) pixmap = pixmap.scaled( cr.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation ); m_iconLabel->setPixmap( pixmap ); emit iconChanged( m_icon ); } AnnotationWidget * AnnotationWidgetFactory::widgetFor( Okular::Annotation * ann ) { switch ( ann->subType() ) { case Okular::Annotation::AStamp: return new StampAnnotationWidget( ann ); break; case Okular::Annotation::AText: return new TextAnnotationWidget( ann ); break; case Okular::Annotation::ALine: return new LineAnnotationWidget( ann ); break; case Okular::Annotation::AHighlight: return new HighlightAnnotationWidget( ann ); break; case Okular::Annotation::AInk: return new InkAnnotationWidget( ann ); break; case Okular::Annotation::AGeom: return new GeomAnnotationWidget( ann ); break; case Okular::Annotation::AFileAttachment: return new FileAttachmentAnnotationWidget( ann ); break; case Okular::Annotation::ACaret: return new CaretAnnotationWidget( ann ); break; // shut up gcc default: ; } // cases not covered yet: return a generic widget return new AnnotationWidget( ann ); } AnnotationWidget::AnnotationWidget( Okular::Annotation * ann ) : QObject(), m_ann( ann ), m_appearanceWidget( nullptr ), m_extraWidget( nullptr ) { + m_textAnn = static_cast< Okular::TextAnnotation * >( ann ); } AnnotationWidget::~AnnotationWidget() { } Okular::Annotation::SubType AnnotationWidget::annotationType() const { return m_ann->subType(); } QWidget * AnnotationWidget::appearanceWidget() { if ( m_appearanceWidget ) return m_appearanceWidget; m_appearanceWidget = createAppearanceWidget(); return m_appearanceWidget; } QWidget * AnnotationWidget::extraWidget() { if ( m_extraWidget ) return m_extraWidget; m_extraWidget = createExtraWidget(); return m_extraWidget; } void AnnotationWidget::applyChanges() { + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + return; m_ann->style().setColor( m_colorBn->color() ); m_ann->style().setOpacity( (double)m_opacity->value() / 100.0 ); } QWidget * AnnotationWidget::createAppearanceWidget() { QWidget * widget = new QWidget(); QGridLayout * gridlayout = new QGridLayout( widget ); - - QLabel * tmplabel = new QLabel( i18n( "&Color:" ), widget ); - gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); - m_colorBn = new KColorButton( widget ); - m_colorBn->setColor( m_ann->style().color() ); - tmplabel->setBuddy( m_colorBn ); - gridlayout->addWidget( m_colorBn, 0, 1 ); - - tmplabel = new QLabel( i18n( "&Opacity:" ), widget ); - gridlayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); - m_opacity = new QSpinBox( widget ); - m_opacity->setRange( 0, 100 ); - m_opacity->setValue( (int)( m_ann->style().opacity() * 100 ) ); - m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80 %'", " %" ) ); - tmplabel->setBuddy( m_opacity ); - gridlayout->addWidget( m_opacity, 1, 1 ); + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::Unknown ) + { + QLabel * tmplabel = new QLabel( i18n( "&Color:" ), widget ); + gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); + m_colorBn = new KColorButton( widget ); + m_colorBn->setColor( m_ann->style().color() ); + tmplabel->setBuddy( m_colorBn ); + gridlayout->addWidget( m_colorBn, 0, 1 ); + + tmplabel = new QLabel( i18n( "&Opacity:" ), widget ); + gridlayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); + m_opacity = new QSpinBox( widget ); + m_opacity->setRange( 0, 100 ); + m_opacity->setValue( (int)( m_ann->style().opacity() * 100 ) ); + m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80 %'", " %" ) ); + tmplabel->setBuddy( m_opacity ); + gridlayout->addWidget( m_opacity, 1, 1 ); + + connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); + connect( m_opacity, SIGNAL(valueChanged(int)), this, SIGNAL(dataChanged()) ); + } QWidget * styleWidget = createStyleWidget(); if ( styleWidget ) gridlayout->addWidget( styleWidget, 2, 0, 1, 2 ); gridlayout->addItem( new QSpacerItem( 5, 5, QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ), 3, 0 ); - connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); - connect( m_opacity, SIGNAL(valueChanged(int)), this, SIGNAL(dataChanged()) ); - return widget; } QWidget * AnnotationWidget::createStyleWidget() { return nullptr; } QWidget * AnnotationWidget::createExtraWidget() { return nullptr; } TextAnnotationWidget::TextAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ), m_pixmapSelector( nullptr ) { m_textAnn = static_cast< Okular::TextAnnotation * >( ann ); } QWidget * TextAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); if ( m_textAnn->textType() == Okular::TextAnnotation::Linked ) { QGroupBox * gb = new QGroupBox( widget ); lay->addWidget( gb ); gb->setTitle( i18n( "Icon" ) ); QHBoxLayout * gblay = new QHBoxLayout( gb ); m_pixmapSelector = new PixmapPreviewSelector( gb ); gblay->addWidget( m_pixmapSelector ); m_pixmapSelector->addItem( i18n( "Comment" ), QStringLiteral("Comment") ); m_pixmapSelector->addItem( i18n( "Help" ), QStringLiteral("Help") ); m_pixmapSelector->addItem( i18n( "Insert" ), QStringLiteral("Insert") ); m_pixmapSelector->addItem( i18n( "Key" ), QStringLiteral("Key") ); m_pixmapSelector->addItem( i18n( "New Paragraph" ), QStringLiteral("NewParagraph") ); m_pixmapSelector->addItem( i18n( "Note" ), QStringLiteral("Note") ); m_pixmapSelector->addItem( i18n( "Paragraph" ), QStringLiteral("Paragraph") ); m_pixmapSelector->setIcon( m_textAnn->textIcon() ); connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged ); } else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace ) { QGridLayout * innerlay = new QGridLayout(); lay->addLayout( innerlay ); QLabel * tmplabel = new QLabel( i18n( "Font:" ), widget ); innerlay->addWidget( tmplabel, 0, 0 ); m_fontReq = new KFontRequester( widget ); innerlay->addWidget( m_fontReq, 0, 1 ); m_fontReq->setFont( m_textAnn->textFont() ); + connect( m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged ); + + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + return widget; tmplabel = new QLabel( i18n( "Align:" ), widget ); innerlay->addWidget( tmplabel, 1, 0 ); m_textAlign = new KComboBox( widget ); innerlay->addWidget( m_textAlign, 1, 1 ); m_textAlign->addItem( i18n("Left") ); m_textAlign->addItem( i18n("Center") ); m_textAlign->addItem( i18n("Right") ); m_textAlign->setCurrentIndex( m_textAnn->inplaceAlignment() ); tmplabel = new QLabel( i18n( "Border Width:" ), widget ); innerlay->addWidget( tmplabel, 2, 0, Qt::AlignRight ); m_spinWidth = new QDoubleSpinBox( widget ); innerlay->addWidget( m_spinWidth, 2, 1 ); tmplabel->setBuddy( m_spinWidth ); m_spinWidth->setRange( 0, 100 ); m_spinWidth->setValue( m_textAnn->style().width() ); m_spinWidth->setSingleStep( 0.1 ); - connect( m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged ); - connect( m_textAlign, SIGNAL(currentIndexChanged(int)), this, SIGNAL(dataChanged()) ); connect( m_spinWidth, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); } return widget; } void TextAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); if ( m_textAnn->textType() == Okular::TextAnnotation::Linked ) { m_textAnn->setTextIcon( m_pixmapSelector->icon() ); } else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace ) { m_textAnn->setTextFont( m_fontReq->font() ); + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + return; m_textAnn->setInplaceAlignment( m_textAlign->currentIndex() ); m_textAnn->style().setWidth( m_spinWidth->value() ); } } StampAnnotationWidget::StampAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ), m_pixmapSelector( nullptr ) { m_stampAnn = static_cast< Okular::StampAnnotation * >( ann ); } QWidget * StampAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); QGroupBox * gb = new QGroupBox( widget ); lay->addWidget( gb ); gb->setTitle( i18n( "Stamp Symbol" ) ); QHBoxLayout * gblay = new QHBoxLayout( gb ); m_pixmapSelector = new PixmapPreviewSelector( gb ); gblay->addWidget( m_pixmapSelector ); m_pixmapSelector->setEditable( true ); m_pixmapSelector->addItem( i18n( "Okular" ), QStringLiteral("okular") ); m_pixmapSelector->addItem( i18n( "Bookmark" ), QStringLiteral("bookmarks") ); m_pixmapSelector->addItem( i18n( "KDE" ), QStringLiteral("kde") ); m_pixmapSelector->addItem( i18n( "Information" ), QStringLiteral("help-about") ); m_pixmapSelector->addItem( i18n( "Approved" ), QStringLiteral("Approved") ); m_pixmapSelector->addItem( i18n( "As Is" ), QStringLiteral("AsIs") ); m_pixmapSelector->addItem( i18n( "Confidential" ), QStringLiteral("Confidential") ); m_pixmapSelector->addItem( i18n( "Departmental" ), QStringLiteral("Departmental") ); m_pixmapSelector->addItem( i18n( "Draft" ), QStringLiteral("Draft") ); m_pixmapSelector->addItem( i18n( "Experimental" ), QStringLiteral("Experimental") ); m_pixmapSelector->addItem( i18n( "Expired" ), QStringLiteral("Expired") ); m_pixmapSelector->addItem( i18n( "Final" ), QStringLiteral("Final") ); m_pixmapSelector->addItem( i18n( "For Comment" ), QStringLiteral("ForComment") ); m_pixmapSelector->addItem( i18n( "For Public Release" ), QStringLiteral("ForPublicRelease") ); m_pixmapSelector->addItem( i18n( "Not Approved" ), QStringLiteral("NotApproved") ); m_pixmapSelector->addItem( i18n( "Not For Public Release" ), QStringLiteral("NotForPublicRelease") ); m_pixmapSelector->addItem( i18n( "Sold" ), QStringLiteral("Sold") ); m_pixmapSelector->addItem( i18n( "Top Secret" ), QStringLiteral("TopSecret") ); m_pixmapSelector->setIcon( m_stampAnn->stampIconName() ); m_pixmapSelector->setPreviewSize( 64 ); connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged ); return widget; } void StampAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); m_stampAnn->setStampIconName( m_pixmapSelector->icon() ); } LineAnnotationWidget::LineAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ) { m_lineAnn = static_cast< Okular::LineAnnotation * >( ann ); if ( m_lineAnn->linePoints().count() == 2 ) m_lineType = 0; // line else if ( m_lineAnn->lineClosed() ) m_lineType = 1; // polygon else m_lineType = 2; // polyline } QWidget * LineAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); if ( m_lineType == 0 ) { QGroupBox * gb = new QGroupBox( widget ); lay->addWidget( gb ); gb->setTitle( i18n( "Line Extensions" ) ); QGridLayout * gridlay = new QGridLayout( gb ); QLabel * tmplabel = new QLabel( i18n( "Leader Line Length:" ), gb ); gridlay->addWidget( tmplabel, 0, 0, Qt::AlignRight ); m_spinLL = new QDoubleSpinBox( gb ); gridlay->addWidget( m_spinLL, 0, 1 ); tmplabel->setBuddy( m_spinLL ); tmplabel = new QLabel( i18n( "Leader Line Extensions Length:" ), gb ); gridlay->addWidget( tmplabel, 1, 0, Qt::AlignRight ); m_spinLLE = new QDoubleSpinBox( gb ); gridlay->addWidget( m_spinLLE, 1, 1 ); tmplabel->setBuddy( m_spinLLE ); } QGroupBox * gb2 = new QGroupBox( widget ); lay->addWidget( gb2 ); gb2->setTitle( i18n( "Style" ) ); QGridLayout * gridlay2 = new QGridLayout( gb2 ); QLabel * tmplabel2 = new QLabel( i18n( "&Size:" ), gb2 ); gridlay2->addWidget( tmplabel2, 0, 0, Qt::AlignRight ); m_spinSize = new QDoubleSpinBox( gb2 ); gridlay2->addWidget( m_spinSize, 0, 1 ); tmplabel2->setBuddy( m_spinSize ); if ( m_lineType == 1 ) { m_useColor = new QCheckBox( i18n( "Inner color:" ), gb2 ); gridlay2->addWidget( m_useColor, 1, 0 ); m_innerColor = new KColorButton( gb2 ); gridlay2->addWidget( m_innerColor, 1, 1 ); } if ( m_lineType == 0 ) { m_spinLL->setRange( -500, 500 ); m_spinLL->setValue( m_lineAnn->lineLeadingForwardPoint() ); m_spinLLE->setRange( 0, 500 ); m_spinLLE->setValue( m_lineAnn->lineLeadingBackwardPoint() ); } else if ( m_lineType == 1 ) { m_innerColor->setColor( m_lineAnn->lineInnerColor() ); if ( m_lineAnn->lineInnerColor().isValid() ) { m_useColor->setChecked( true ); } else { m_innerColor->setEnabled( false ); } } m_spinSize->setRange( 1, 100 ); m_spinSize->setValue( m_lineAnn->style().width() ); if ( m_lineType == 0 ) { connect( m_spinLL, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); connect( m_spinLLE, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); } else if ( m_lineType == 1 ) { connect( m_innerColor, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); connect( m_useColor, &QAbstractButton::toggled, this, &AnnotationWidget::dataChanged ); connect(m_useColor, &QCheckBox::toggled, m_innerColor, &KColorButton::setEnabled); } connect( m_spinSize, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); return widget; } void LineAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); if ( m_lineType == 0 ) { m_lineAnn->setLineLeadingForwardPoint( m_spinLL->value() ); m_lineAnn->setLineLeadingBackwardPoint( m_spinLLE->value() ); } else if ( m_lineType == 1 ) { if ( !m_useColor->isChecked() ) { m_lineAnn->setLineInnerColor( QColor() ); } else { m_lineAnn->setLineInnerColor( m_innerColor->color() ); } } m_lineAnn->style().setWidth( m_spinSize->value() ); } InkAnnotationWidget::InkAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ) { m_inkAnn = static_cast< Okular::InkAnnotation * >( ann ); } QWidget * InkAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); QGroupBox * gb2 = new QGroupBox( widget ); lay->addWidget( gb2 ); gb2->setTitle( i18n( "Style" ) ); QGridLayout * gridlay2 = new QGridLayout( gb2 ); QLabel * tmplabel2 = new QLabel( i18n( "&Size:" ), gb2 ); gridlay2->addWidget( tmplabel2, 0, 0, Qt::AlignRight ); m_spinSize = new QDoubleSpinBox( gb2 ); gridlay2->addWidget( m_spinSize, 0, 1 ); tmplabel2->setBuddy( m_spinSize ); m_spinSize->setRange( 1, 100 ); m_spinSize->setValue( m_inkAnn->style().width() ); connect( m_spinSize, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); return widget; } void InkAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); m_inkAnn->style().setWidth( m_spinSize->value() ); } HighlightAnnotationWidget::HighlightAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ) { m_hlAnn = static_cast< Okular::HighlightAnnotation * >( ann ); } QWidget * HighlightAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); QHBoxLayout * typelay = new QHBoxLayout(); lay->addLayout( typelay ); QLabel * tmplabel = new QLabel( i18n( "Type:" ), widget ); typelay->addWidget( tmplabel, 0, Qt::AlignRight ); m_typeCombo = new KComboBox( widget ); tmplabel->setBuddy( m_typeCombo ); typelay->addWidget( m_typeCombo ); m_typeCombo->addItem( i18n( "Highlight" ) ); m_typeCombo->addItem( i18n( "Squiggle" ) ); m_typeCombo->addItem( i18n( "Underline" ) ); m_typeCombo->addItem( i18n( "Strike out" ) ); m_typeCombo->setCurrentIndex( m_hlAnn->highlightType() ); connect( m_typeCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(dataChanged()) ); return widget; } void HighlightAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); m_hlAnn->setHighlightType( (Okular::HighlightAnnotation::HighlightType)m_typeCombo->currentIndex() ); } GeomAnnotationWidget::GeomAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ) { m_geomAnn = static_cast< Okular::GeomAnnotation * >( ann ); } QWidget * GeomAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QGridLayout * lay = new QGridLayout( widget ); lay->setMargin( 0 ); QLabel * tmplabel = new QLabel( i18n( "Type:" ), widget ); lay->addWidget( tmplabel, 0, 0, Qt::AlignRight ); m_typeCombo = new KComboBox( widget ); tmplabel->setBuddy( m_typeCombo ); lay->addWidget( m_typeCombo, 0, 1 ); m_useColor = new QCheckBox( i18n( "Inner color:" ), widget ); lay->addWidget( m_useColor, 1, 0 ); m_innerColor = new KColorButton( widget ); lay->addWidget( m_innerColor, 1, 1 ); tmplabel = new QLabel( i18n( "&Size:" ), widget ); lay->addWidget( tmplabel, 2, 0, Qt::AlignRight ); m_spinSize = new QDoubleSpinBox( widget ); lay->addWidget( m_spinSize, 2, 1 ); tmplabel->setBuddy( m_spinSize ); m_typeCombo->addItem( i18n( "Rectangle" ) ); m_typeCombo->addItem( i18n( "Ellipse" ) ); m_typeCombo->setCurrentIndex( m_geomAnn->geometricalType() ); m_innerColor->setColor( m_geomAnn->geometricalInnerColor() ); if ( m_geomAnn->geometricalInnerColor().isValid() ) { m_useColor->setChecked( true ); } else { m_innerColor->setEnabled( false ); } m_spinSize->setRange( 0, 100 ); m_spinSize->setValue( m_geomAnn->style().width() ); connect( m_typeCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(dataChanged()) ); connect( m_innerColor, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); connect( m_useColor, &QAbstractButton::toggled, this, &AnnotationWidget::dataChanged ); connect(m_useColor, &QCheckBox::toggled, m_innerColor, &KColorButton::setEnabled); connect( m_spinSize, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); return widget; } void GeomAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); m_geomAnn->setGeometricalType( (Okular::GeomAnnotation::GeomType)m_typeCombo->currentIndex() ); if ( !m_useColor->isChecked() ) { m_geomAnn->setGeometricalInnerColor( QColor() ); } else { m_geomAnn->setGeometricalInnerColor( m_innerColor->color() ); } m_geomAnn->style().setWidth( m_spinSize->value() ); } FileAttachmentAnnotationWidget::FileAttachmentAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ), m_pixmapSelector( nullptr ) { m_attachAnn = static_cast< Okular::FileAttachmentAnnotation * >( ann ); } QWidget * FileAttachmentAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); QGroupBox * gb = new QGroupBox( widget ); lay->addWidget( gb ); gb->setTitle( i18n( "File Attachment Symbol" ) ); QHBoxLayout * gblay = new QHBoxLayout( gb ); m_pixmapSelector = new PixmapPreviewSelector( gb ); gblay->addWidget( m_pixmapSelector ); m_pixmapSelector->setEditable( true ); m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Graph" ), QStringLiteral("graph") ); m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Push Pin" ), QStringLiteral("pushpin") ); m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Paperclip" ), QStringLiteral("paperclip") ); m_pixmapSelector->addItem( i18nc( "Symbol for file attachment annotations", "Tag" ), QStringLiteral("tag") ); m_pixmapSelector->setIcon( m_attachAnn->fileIconName() ); connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged ); return widget; } QWidget * FileAttachmentAnnotationWidget::createExtraWidget() { QWidget * widget = new QWidget(); widget->setWindowTitle( i18nc( "'File' as normal file, that can be opened, saved, etc..", "File" ) ); Okular::EmbeddedFile *ef = m_attachAnn->embeddedFile(); const int size = ef->size(); const QString sizeString = size <= 0 ? i18nc( "Not available size", "N/A" ) : KFormat().formatByteSize( size ); const QString descString = ef->description().isEmpty() ? i18n( "No description available." ) : ef->description(); QGridLayout * lay = new QGridLayout( widget ); lay->setMargin( 0 ); QLabel * tmplabel = new QLabel( i18n( "Name: %1", ef->name() ), widget ); tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse ); lay->addWidget( tmplabel, 0, 0 ); tmplabel = new QLabel( i18n( "Size: %1", sizeString ), widget ); tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse ); lay->addWidget( tmplabel, 1, 0 ); tmplabel = new QLabel( i18n( "Description:" ), widget ); lay->addWidget( tmplabel, 2, 0 ); tmplabel = new QLabel( widget ); tmplabel->setTextFormat( Qt::PlainText ); tmplabel->setWordWrap( true ); tmplabel->setText( descString ); tmplabel->setTextInteractionFlags( Qt::TextSelectableByMouse ); lay->addWidget( tmplabel, 3, 0, 1, 2 ); QMimeDatabase db; QMimeType mime = db.mimeTypeForFile( ef->name(), QMimeDatabase::MatchExtension); if ( mime.isValid() ) { tmplabel = new QLabel( widget ); tmplabel->setPixmap( QIcon::fromTheme( mime.iconName() ).pixmap( FILEATTACH_ICONSIZE, FILEATTACH_ICONSIZE ) ); tmplabel->setFixedSize( FILEATTACH_ICONSIZE, FILEATTACH_ICONSIZE ); lay->addWidget( tmplabel, 0, 1, 3, 1, Qt::AlignTop ); } lay->addItem( new QSpacerItem( 5, 5, QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ), 4, 0 ); return widget; } void FileAttachmentAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); m_attachAnn->setFileIconName( m_pixmapSelector->icon() ); } static QString caretSymbolToIcon( Okular::CaretAnnotation::CaretSymbol symbol ) { switch ( symbol ) { case Okular::CaretAnnotation::None: return QStringLiteral( "caret-none" ); case Okular::CaretAnnotation::P: return QStringLiteral( "caret-p" ); } return QString(); } static Okular::CaretAnnotation::CaretSymbol caretSymbolFromIcon( const QString &icon ) { if ( icon == QLatin1String( "caret-none" ) ) return Okular::CaretAnnotation::None; else if ( icon == QLatin1String( "caret-p" ) ) return Okular::CaretAnnotation::P; return Okular::CaretAnnotation::None; } CaretAnnotationWidget::CaretAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ), m_pixmapSelector( nullptr ) { m_caretAnn = static_cast< Okular::CaretAnnotation * >( ann ); } QWidget * CaretAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * lay = new QVBoxLayout( widget ); lay->setMargin( 0 ); QGroupBox * gb = new QGroupBox( widget ); lay->addWidget( gb ); gb->setTitle( i18n( "Caret Symbol" ) ); QHBoxLayout * gblay = new QHBoxLayout( gb ); m_pixmapSelector = new PixmapPreviewSelector( gb ); gblay->addWidget( m_pixmapSelector ); m_pixmapSelector->addItem( i18nc( "Symbol for caret annotations", "None" ), QStringLiteral("caret-none") ); m_pixmapSelector->addItem( i18nc( "Symbol for caret annotations", "P" ), QStringLiteral("caret-p") ); m_pixmapSelector->setIcon( caretSymbolToIcon( m_caretAnn->caretSymbol() ) ); connect( m_pixmapSelector, &PixmapPreviewSelector::iconChanged, this, &AnnotationWidget::dataChanged ); return widget; } void CaretAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); m_caretAnn->setCaretSymbol( caretSymbolFromIcon( m_pixmapSelector->icon() ) ); } #include "moc_annotationwidgets.cpp" diff --git a/ui/annotationwidgets.h b/ui/annotationwidgets.h index 13442ce47..c079030c4 100644 --- a/ui/annotationwidgets.h +++ b/ui/annotationwidgets.h @@ -1,257 +1,258 @@ /*************************************************************************** * Copyright (C) 2006 by Pino Toscano * * * * 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. * ***************************************************************************/ #ifndef _ANNOTATIONWIDGETS_H_ #define _ANNOTATIONWIDGETS_H_ #include #include "core/annotations.h" class QCheckBox; class QComboBox; class QDoubleSpinBox; class QLabel; class QWidget; class KColorButton; class QSpinBox; class KFontRequester; class AnnotationWidget; class PixmapPreviewSelector : public QWidget { Q_OBJECT public: explicit PixmapPreviewSelector( QWidget * parent = nullptr ); virtual ~PixmapPreviewSelector(); void setIcon( const QString& icon ); QString icon() const; void addItem( const QString& item, const QString& id ); void setPreviewSize( int size ); int previewSize() const; void setEditable( bool editable ); Q_SIGNALS: void iconChanged( const QString& ); private Q_SLOTS: void iconComboChanged( const QString& icon ); private: QString m_icon; QLabel * m_iconLabel; QComboBox * m_comboItems; int m_previewSize; }; /** * A factory to create AnnotationWidget's. */ class AnnotationWidgetFactory { public: static AnnotationWidget * widgetFor( Okular::Annotation * ann ); }; class AnnotationWidget : public QObject { Q_OBJECT public: AnnotationWidget( Okular::Annotation * ann ); virtual ~AnnotationWidget(); virtual Okular::Annotation::SubType annotationType() const; QWidget * appearanceWidget(); QWidget * extraWidget(); virtual void applyChanges(); Q_SIGNALS: void dataChanged(); protected: QWidget * createAppearanceWidget(); virtual QWidget * createStyleWidget(); virtual QWidget * createExtraWidget(); Okular::Annotation * m_ann; + Okular::TextAnnotation * m_textAnn; QWidget * m_appearanceWidget; QWidget * m_extraWidget; KColorButton *m_colorBn; QSpinBox *m_opacity; }; class TextAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit TextAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::TextAnnotation * m_textAnn; PixmapPreviewSelector * m_pixmapSelector; KFontRequester * m_fontReq; QComboBox * m_textAlign; QDoubleSpinBox * m_spinWidth; }; class StampAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit StampAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::StampAnnotation * m_stampAnn; PixmapPreviewSelector * m_pixmapSelector; }; class LineAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit LineAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::LineAnnotation * m_lineAnn; int m_lineType; QDoubleSpinBox * m_spinLL; QDoubleSpinBox * m_spinLLE; QCheckBox * m_useColor; KColorButton * m_innerColor; QDoubleSpinBox * m_spinSize; }; class HighlightAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit HighlightAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::HighlightAnnotation * m_hlAnn; QComboBox * m_typeCombo; }; class GeomAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit GeomAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::GeomAnnotation * m_geomAnn; QComboBox * m_typeCombo; QCheckBox * m_useColor; KColorButton * m_innerColor; QDoubleSpinBox * m_spinSize; }; class FileAttachmentAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit FileAttachmentAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; QWidget * createExtraWidget() override; private: Okular::FileAttachmentAnnotation * m_attachAnn; PixmapPreviewSelector * m_pixmapSelector; }; class CaretAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit CaretAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::CaretAnnotation * m_caretAnn; PixmapPreviewSelector * m_pixmapSelector; }; class InkAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit InkAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: Okular::InkAnnotation * m_inkAnn; QDoubleSpinBox * m_spinSize; }; #endif diff --git a/ui/annotwindow.cpp b/ui/annotwindow.cpp index d069a3c1e..10ed5e294 100644 --- a/ui/annotwindow.cpp +++ b/ui/annotwindow.cpp @@ -1,434 +1,434 @@ /*************************************************************************** * Copyright (C) 2006 by Chu Xiaodong * * Copyright (C) 2006 by Pino Toscano * * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group * * company, info@kdab.com. Work sponsored by the * * LiMux project of the city of Munich * * * * 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 "annotwindow.h" // qt/kde includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // local includes #include "core/annotations.h" #include "core/document.h" #include "latexrenderer.h" #include #include class CloseButton : public QPushButton { Q_OBJECT public: CloseButton( QWidget * parent = Q_NULLPTR ) : QPushButton( parent ) { setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); QSize size = QSize( 14, 14 ).expandedTo( QApplication::globalStrut() ); setFixedSize( size ); setIcon( style()->standardIcon( QStyle::SP_DockWidgetCloseButton ) ); setIconSize( size ); setToolTip( i18n( "Close this note" ) ); setCursor( Qt::ArrowCursor ); } }; class MovableTitle : public QWidget { Q_OBJECT public: MovableTitle( QWidget * parent ) : QWidget( parent ) { QVBoxLayout * mainlay = new QVBoxLayout( this ); mainlay->setMargin( 0 ); mainlay->setSpacing( 0 ); // close button row QHBoxLayout * buttonlay = new QHBoxLayout(); mainlay->addLayout( buttonlay ); titleLabel = new QLabel( this ); QFont f = titleLabel->font(); f.setBold( true ); titleLabel->setFont( f ); titleLabel->setCursor( Qt::SizeAllCursor ); buttonlay->addWidget( titleLabel ); dateLabel = new QLabel( this ); dateLabel->setAlignment( Qt::AlignTop | Qt::AlignRight ); f = dateLabel->font(); f.setPointSize( QFontInfo( f ).pointSize() - 2 ); dateLabel->setFont( f ); dateLabel->setCursor( Qt::SizeAllCursor ); buttonlay->addWidget( dateLabel ); CloseButton * close = new CloseButton( this ); connect( close, &QAbstractButton::clicked, parent, &QWidget::close ); buttonlay->addWidget( close ); // option button row QHBoxLayout * optionlay = new QHBoxLayout(); mainlay->addLayout( optionlay ); authorLabel = new QLabel( this ); authorLabel->setCursor( Qt::SizeAllCursor ); authorLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum ); optionlay->addWidget( authorLabel ); optionButton = new QToolButton( this ); QString opttext = i18n( "Options" ); optionButton->setText( opttext ); optionButton->setAutoRaise( true ); QSize s = QFontMetrics( optionButton->font() ).boundingRect( opttext ).size() + QSize( 8, 8 ); optionButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); optionButton->setFixedSize( s ); optionlay->addWidget( optionButton ); // ### disabled for now optionButton->hide(); latexButton = new QToolButton( this ); QHBoxLayout * latexlay = new QHBoxLayout(); QString latextext = i18n ( "This annotation may contain LaTeX code.\nClick here to render." ); latexButton->setText( latextext ); latexButton->setAutoRaise( true ); s = QFontMetrics( latexButton->font() ).boundingRect(0, 0, this->width(), this->height(), 0, latextext ).size() + QSize( 8, 8 ); latexButton->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); latexButton->setFixedSize( s ); latexButton->setCheckable( true ); latexButton->setVisible( false ); latexlay->addSpacing( 1 ); latexlay->addWidget( latexButton ); latexlay->addSpacing( 1 ); mainlay->addLayout( latexlay ); connect(latexButton, SIGNAL(clicked(bool)), parent, SLOT(renderLatex(bool))); connect(parent, SIGNAL(containsLatex(bool)), latexButton, SLOT(setVisible(bool))); titleLabel->installEventFilter( this ); dateLabel->installEventFilter( this ); authorLabel->installEventFilter( this ); } bool eventFilter( QObject * obj, QEvent * e ) override { if ( obj != titleLabel && obj != authorLabel && obj != dateLabel ) return false; QMouseEvent * me = nullptr; switch ( e->type() ) { case QEvent::MouseButtonPress: me = (QMouseEvent*)e; mousePressPos = me->pos(); parentWidget()->raise(); break; case QEvent::MouseButtonRelease: mousePressPos = QPoint(); break; case QEvent::MouseMove: me = (QMouseEvent*)e; parentWidget()->move( me->pos() - mousePressPos + parentWidget()->pos() ); break; default: return false; } return true; } void setTitle( const QString& title ) { titleLabel->setText( QStringLiteral( " " ) + title ); } void setDate( const QDateTime& dt ) { dateLabel->setText( QLocale().toString( dt, QLocale::ShortFormat ) + QLatin1Char(' ') ); } void setAuthor( const QString& author ) { authorLabel->setText( QStringLiteral( " " ) + author ); } void connectOptionButton( QObject * recv, const char* method ) { connect( optionButton, SIGNAL(clicked()), recv, method ); } void uncheckLatexButton() { latexButton->setChecked( false ); } private: QLabel * titleLabel; QLabel * dateLabel; QLabel * authorLabel; QPoint mousePressPos; QToolButton * optionButton; QToolButton * latexButton; }; // Qt::SubWindow is needed to make QSizeGrip work AnnotWindow::AnnotWindow( QWidget * parent, Okular::Annotation * annot, Okular::Document *document, int page ) : QFrame( parent, Qt::SubWindow ), m_annot( annot ), m_document( document ), m_page( page ) { setAutoFillBackground( true ); setFrameStyle( Panel | Raised ); setAttribute( Qt::WA_DeleteOnClose ); setObjectName("AnnotWindow"); const bool canEditAnnotation = m_document->canModifyPageAnnotation( annot ); textEdit = new KTextEdit( this ); textEdit->setAcceptRichText( false ); textEdit->setPlainText( m_annot->contents() ); textEdit->installEventFilter( this ); textEdit->setUndoRedoEnabled( false ); m_prevCursorPos = textEdit->textCursor().position(); m_prevAnchorPos = textEdit->textCursor().anchor(); connect(textEdit, &KTextEdit::textChanged, this, &AnnotWindow::slotsaveWindowText); connect(textEdit, &KTextEdit::cursorPositionChanged, this, &AnnotWindow::slotsaveWindowText); connect(textEdit, &KTextEdit::aboutToShowContextMenu, this, &AnnotWindow::slotUpdateUndoAndRedoInContextMenu); connect(m_document, &Okular::Document::annotationContentsChangedByUndoRedo, this, &AnnotWindow::slotHandleContentsChangedByUndoRedo); if (!canEditAnnotation) textEdit->setReadOnly(true); QVBoxLayout * mainlay = new QVBoxLayout( this ); mainlay->setMargin( 2 ); mainlay->setSpacing( 0 ); m_title = new MovableTitle( this ); mainlay->addWidget( m_title ); mainlay->addWidget( textEdit ); QHBoxLayout * lowerlay = new QHBoxLayout(); mainlay->addLayout( lowerlay ); lowerlay->addItem( new QSpacerItem( 5, 5, QSizePolicy::Expanding, QSizePolicy::Fixed ) ); QSizeGrip * sb = new QSizeGrip( this ); lowerlay->addWidget( sb ); m_latexRenderer = new GuiUtils::LatexRenderer(); emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( m_annot->contents() ) ); m_title->setTitle( m_annot->window().summary() ); m_title->connectOptionButton( this, SLOT(slotOptionBtn()) ); setGeometry(10,10,300,300 ); reloadInfo(); } AnnotWindow::~AnnotWindow() { delete m_latexRenderer; } Okular::Annotation * AnnotWindow::annotation() const { return m_annot; } void AnnotWindow::updateAnnotation( Okular::Annotation * a ) { m_annot = a; } void AnnotWindow::reloadInfo() { - const QColor newcolor = m_annot->style().color().isValid() ? m_annot->style().color() : Qt::yellow; + const QColor newcolor = m_annot->style().color().isValid() ? QColor(m_annot->style().color().name()) : Qt::yellow; if ( newcolor != m_color ) { m_color = newcolor; setPalette( QPalette( m_color ) ); QPalette pl = textEdit->palette(); pl.setColor( QPalette::Base, m_color ); textEdit->setPalette( pl ); } m_title->setAuthor( m_annot->author() ); m_title->setDate( m_annot->modificationDate() ); } int AnnotWindow::pageNumber() const { return m_page; } void AnnotWindow::showEvent( QShowEvent * event ) { QFrame::showEvent( event ); // focus the content area by default textEdit->setFocus(); } bool AnnotWindow::eventFilter(QObject *, QEvent *e) { if ( e->type () == QEvent::ShortcutOverride ) { QKeyEvent * keyEvent = static_cast< QKeyEvent * >( e ); if ( keyEvent->key() == Qt::Key_Escape ) { close(); return true; } } else if (e->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(e); if (keyEvent == QKeySequence::Undo) { m_document->undo(); return true; } else if (keyEvent == QKeySequence::Redo) { m_document->redo(); return true; } } else if (e->type() == QEvent::FocusIn) { raise(); } return false; } void AnnotWindow::slotUpdateUndoAndRedoInContextMenu(QMenu* menu) { if (!menu) return; QList actionList = menu->actions(); enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs }; QAction *kundo = KStandardAction::create( KStandardAction::Undo, m_document, SLOT(undo()), menu); QAction *kredo = KStandardAction::create( KStandardAction::Redo, m_document, SLOT(redo()), menu); connect(m_document, &Okular::Document::canUndoChanged, kundo, &QAction::setEnabled); connect(m_document, &Okular::Document::canRedoChanged, kredo, &QAction::setEnabled); kundo->setEnabled(m_document->canUndo()); kredo->setEnabled(m_document->canRedo()); QAction *oldUndo, *oldRedo; oldUndo = actionList[UndoAct]; oldRedo = actionList[RedoAct]; menu->insertAction(oldUndo, kundo); menu->insertAction(oldRedo, kredo); menu->removeAction(oldUndo); menu->removeAction(oldRedo); } void AnnotWindow::slotOptionBtn() { //TODO: call context menu in pageview //emit sig... } void AnnotWindow::slotsaveWindowText() { const QString contents = textEdit->toPlainText(); const int cursorPos = textEdit->textCursor().position(); if (contents != m_annot->contents()) { m_document->editPageAnnotationContents( m_page, m_annot, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos); emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( textEdit->toPlainText() ) ); } m_prevCursorPos = cursorPos; m_prevAnchorPos = textEdit->textCursor().anchor(); } void AnnotWindow::renderLatex( bool render ) { if (render) { textEdit->setReadOnly( true ); disconnect(textEdit, &KTextEdit::textChanged, this, &AnnotWindow::slotsaveWindowText); disconnect(textEdit, &KTextEdit::cursorPositionChanged, this, &AnnotWindow::slotsaveWindowText); textEdit->setAcceptRichText( true ); QString contents = m_annot->contents(); contents = Qt::convertFromPlainText( contents ); QColor fontColor = textEdit->textColor(); int fontSize = textEdit->fontPointSize(); QString latexOutput; GuiUtils::LatexRenderer::Error errorCode = m_latexRenderer->renderLatexInHtml( contents, fontColor, fontSize, Okular::Utils::realDpi(nullptr).width(), latexOutput ); switch ( errorCode ) { case GuiUtils::LatexRenderer::LatexNotFound: KMessageBox::sorry( this, i18n( "Cannot find latex executable." ), i18n( "LaTeX rendering failed" ) ); m_title->uncheckLatexButton(); renderLatex( false ); break; case GuiUtils::LatexRenderer::DvipngNotFound: KMessageBox::sorry( this, i18n( "Cannot find dvipng executable." ), i18n( "LaTeX rendering failed" ) ); m_title->uncheckLatexButton(); renderLatex( false ); break; case GuiUtils::LatexRenderer::LatexFailed: KMessageBox::detailedSorry( this, i18n( "A problem occurred during the execution of the 'latex' command." ), latexOutput, i18n( "LaTeX rendering failed" ) ); m_title->uncheckLatexButton(); renderLatex( false ); break; case GuiUtils::LatexRenderer::DvipngFailed: KMessageBox::sorry( this, i18n( "A problem occurred during the execution of the 'dvipng' command." ), i18n( "LaTeX rendering failed" ) ); m_title->uncheckLatexButton(); renderLatex( false ); break; case GuiUtils::LatexRenderer::NoError: default: textEdit->setHtml( contents ); break; } } else { textEdit->setAcceptRichText( false ); textEdit->setPlainText( m_annot->contents() ); connect(textEdit, &KTextEdit::textChanged, this, &AnnotWindow::slotsaveWindowText); connect(textEdit, &KTextEdit::cursorPositionChanged, this, &AnnotWindow::slotsaveWindowText); textEdit->setReadOnly( false ); } } void AnnotWindow::slotHandleContentsChangedByUndoRedo(Okular::Annotation* annot, QString contents, int cursorPos, int anchorPos) { if ( annot != m_annot ) { return; } textEdit->setPlainText(contents); QTextCursor c = textEdit->textCursor(); c.setPosition(anchorPos); c.setPosition(cursorPos,QTextCursor::KeepAnchor); m_prevCursorPos = cursorPos; m_prevAnchorPos = anchorPos; textEdit->setTextCursor(c); textEdit->setFocus(); emit containsLatex( GuiUtils::LatexRenderer::mightContainLatex( m_annot->contents() ) ); } #include "annotwindow.moc" diff --git a/ui/data/CMakeLists.txt b/ui/data/CMakeLists.txt index e177d515e..b40cbb0c4 100644 --- a/ui/data/CMakeLists.txt +++ b/ui/data/CMakeLists.txt @@ -1,57 +1,59 @@ # an own directory for multi-size icons retrieved by KIconThemeLoader add_subdirectory(icons) # install annotator xml tools description install(FILES tools.xml drawingtools.xml DESTINATION ${KDE_INSTALL_DATADIR}/okular) # install annotation tool images install(FILES tool-base-okular.png tool-base-okular@2x.png tool-highlighter-okular-colorizable.png tool-highlighter-okular-colorizable@2x.png tool-ink-okular-colorizable.png tool-ink-okular-colorizable@2x.png tool-note.png tool-note-okular-colorizable.png tool-note-okular-colorizable@2x.png tool-note-inline.png tool-note-inline-okular-colorizable.png tool-note-inline-okular-colorizable@2x.png + tool-typewriter-okular-colorizable.png + tool-typewriter-okular-colorizable@2x.png DESTINATION ${KDE_INSTALL_DATADIR}/okular/pics) # install annotation page images install(FILES checkmark.png circle.png comment.png cross.png help.png insert.png key.png newparagraph.png note.png paperclip.png paragraph.png pushpin.png rightarrow.png rightpointer.png star.png uparrow.png upleftarrow.png DESTINATION ${KDE_INSTALL_DATADIR}/okular/pics) # install annotation stamps install(FILES stamps.svg DESTINATION ${KDE_INSTALL_DATADIR}/okular/pics) # install misc images # newstuff if (FALSE) install(FILES okular.knsrc DESTINATION ${KDE_INSTALL_CONFDIR}) endif (FALSE) diff --git a/ui/data/sources/tool-typewriter-okular-colorizable.svgz b/ui/data/sources/tool-typewriter-okular-colorizable.svgz new file mode 100644 index 000000000..8bb05eba3 Binary files /dev/null and b/ui/data/sources/tool-typewriter-okular-colorizable.svgz differ diff --git a/ui/data/tool-typewriter-okular-colorizable.png b/ui/data/tool-typewriter-okular-colorizable.png new file mode 100644 index 000000000..89da4aca3 Binary files /dev/null and b/ui/data/tool-typewriter-okular-colorizable.png differ diff --git a/ui/data/tool-typewriter-okular-colorizable@2x.png b/ui/data/tool-typewriter-okular-colorizable@2x.png new file mode 100644 index 000000000..c3961615e Binary files /dev/null and b/ui/data/tool-typewriter-okular-colorizable@2x.png differ diff --git a/ui/data/tools.xml b/ui/data/tools.xml index 1fd590481..01de6d854 100644 --- a/ui/data/tools.xml +++ b/ui/data/tools.xml @@ -1,74 +1,79 @@ 1 2 3 4 5 6 7 8 9 + + + + + diff --git a/ui/guiutils.cpp b/ui/guiutils.cpp index 45656453b..c184e027c 100644 --- a/ui/guiutils.cpp +++ b/ui/guiutils.cpp @@ -1,280 +1,285 @@ /*************************************************************************** * Copyright (C) 2006-2007 by Pino Toscano * * * * 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 "guiutils.h" // qt/kde includes #include #include #include #include #include #include #include #include // local includes #include "core/action.h" #include "core/annotations.h" #include "core/document.h" #include struct GuiUtilsHelper { GuiUtilsHelper() { } QSvgRenderer* svgStamps(); QList il; std::unique_ptr< QSvgRenderer > svgStampFile; }; QSvgRenderer* GuiUtilsHelper::svgStamps() { if ( !svgStampFile.get() ) { const QString stampFile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("okular/pics/stamps.svg") ); if ( !stampFile.isEmpty() ) { svgStampFile.reset( new QSvgRenderer( stampFile ) ); if ( !svgStampFile->isValid() ) { svgStampFile.reset(); } } } return svgStampFile.get(); } Q_GLOBAL_STATIC( GuiUtilsHelper, s_data ) namespace GuiUtils { QString captionForAnnotation( const Okular::Annotation * ann ) { Q_ASSERT( ann ); const bool hasComment = !ann->contents().isEmpty(); QString ret; switch( ann->subType() ) { case Okular::Annotation::AText: if( ( (Okular::TextAnnotation*)ann )->textType() == Okular::TextAnnotation::Linked ) ret = i18n( "Pop-up Note" ); else - ret = i18n( "Inline Note" ); + { + if( ( (Okular::TextAnnotation*)ann )->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + ret = i18n( "Typewriter" ); + else + ret = i18n( "Inline Note" ); + } break; case Okular::Annotation::ALine: if( ( (Okular::LineAnnotation*)ann )->linePoints().count() == 2 ) ret = hasComment ? i18n( "Straight Line with Comment" ) : i18n( "Straight Line" ); else ret = hasComment ? i18n( "Polygon with Comment" ) : i18n( "Polygon" ); break; case Okular::Annotation::AGeom: ret = hasComment ? i18n( "Geometry with Comment" ) : i18n( "Geometry" ); break; case Okular::Annotation::AHighlight: switch ( ( (Okular::HighlightAnnotation*)ann )->highlightType() ) { case Okular::HighlightAnnotation::Highlight: ret = hasComment ? i18n( "Highlight with Comment" ) : i18n( "Highlight" ); break; case Okular::HighlightAnnotation::Squiggly: ret = hasComment ? i18n( "Squiggle with Comment" ) : i18n( "Squiggle" ); break; case Okular::HighlightAnnotation::Underline: ret = hasComment ? i18n( "Underline with Comment" ) : i18n( "Underline" ); break; case Okular::HighlightAnnotation::StrikeOut: ret = hasComment ? i18n( "Strike Out with Comment" ) : i18n( "Strike Out" ); break; } break; case Okular::Annotation::AStamp: ret = hasComment ? i18n( "Stamp with Comment" ) : i18n( "Stamp" ); break; case Okular::Annotation::AInk: ret = hasComment ? i18n( "Freehand Line with Comment" ) : i18n( "Freehand Line" ); break; case Okular::Annotation::ACaret: ret = i18n( "Caret" ); break; case Okular::Annotation::AFileAttachment: ret = i18n( "File Attachment" ); break; case Okular::Annotation::ASound: ret = i18n( "Sound" ); break; case Okular::Annotation::AMovie: ret = i18n( "Movie" ); break; case Okular::Annotation::AScreen: ret = i18nc( "Caption for a screen annotation", "Screen" ); break; case Okular::Annotation::AWidget: ret = i18nc( "Caption for a widget annotation", "Widget" ); break; case Okular::Annotation::ARichMedia: ret = i18nc( "Caption for a rich media annotation", "Rich Media" ); break; case Okular::Annotation::A_BASE: break; } return ret; } QString authorForAnnotation( const Okular::Annotation * ann ) { Q_ASSERT( ann ); return !ann->author().isEmpty() ? ann->author() : i18nc( "Unknown author", "Unknown" ); } QString contentsHtml( const Okular::Annotation * ann ) { QString text = ann->contents().toHtmlEscaped(); text.replace( QLatin1Char('\n'), QLatin1String("
") ); return text; } QString prettyToolTip( const Okular::Annotation * ann ) { Q_ASSERT( ann ); QString author = authorForAnnotation( ann ); QString contents = contentsHtml( ann ); QString tooltip = QStringLiteral( "" ) + i18n( "Author: %1", author ) + QStringLiteral( "" ); if ( !contents.isEmpty() ) tooltip += QStringLiteral( "

" ) + contents; tooltip += QLatin1String("
"); return tooltip; } QPixmap loadStamp( const QString& _name, const QSize& size, int iconSize ) { const QString name = _name.toLower(); QSvgRenderer * r = nullptr; if ( ( r = s_data->svgStamps() ) && r->elementExists( name ) ) { const QRectF stampElemRect = r->boundsOnElement( name ); const QRectF stampRect( size.isValid() ? QRectF( QPointF( 0, 0 ), size ) : stampElemRect ); QPixmap pixmap( stampRect.size().toSize() ); pixmap.fill( Qt::transparent ); QPainter p( &pixmap ); r->render( &p, name ); p.end(); return pixmap; } QPixmap pixmap; const KIconLoader * il = iconLoader(); QString path; const int minSize = iconSize > 0 ? iconSize : qMin( size.width(), size.height() ); pixmap = il->loadIcon( name, KIconLoader::User, minSize, KIconLoader::DefaultState, QStringList(), &path, true ); if ( path.isEmpty() ) pixmap = il->loadIcon( name, KIconLoader::NoGroup, minSize ); return pixmap; } void addIconLoader( KIconLoader * loader ) { s_data->il.append( loader ); } void removeIconLoader( KIconLoader * loader ) { s_data->il.removeAll( loader ); } KIconLoader* iconLoader() { return s_data->il.isEmpty() ? KIconLoader::global() : s_data->il.back(); } void saveEmbeddedFile( Okular::EmbeddedFile *ef, QWidget *parent ) { const QString caption = i18n( "Where do you want to save %1?", ef->name() ); const QString path = QFileDialog::getSaveFileName( parent, caption, ef->name() ); if ( path.isEmpty() ) return; QFile targetFile( path ); writeEmbeddedFile( ef, parent, targetFile ); } void writeEmbeddedFile( Okular::EmbeddedFile *ef, QWidget *parent, QFile& target ) { if ( !target.open( QIODevice::WriteOnly ) ) { KMessageBox::error( parent, i18n( "Could not open \"%1\" for writing. File was not saved.", target.fileName() ) ); return; } target.write( ef->data() ); target.close(); } Okular::Movie* renditionMovieFromScreenAnnotation( const Okular::ScreenAnnotation *annotation ) { if ( !annotation ) return nullptr; if ( annotation->action() && annotation->action()->actionType() == Okular::Action::Rendition ) { Okular::RenditionAction *renditionAction = static_cast< Okular::RenditionAction * >( annotation->action() ); return renditionAction->movie(); } return nullptr; } // from Arthur - qt4 static inline int qt_div_255(int x) { return (x + (x>>8) + 0x80) >> 8; } void colorizeImage( QImage & grayImage, const QColor & color, unsigned int destAlpha ) { // Make sure that the image is Format_ARGB32_Premultiplied if ( grayImage.format() != QImage::Format_ARGB32_Premultiplied ) grayImage = grayImage.convertToFormat( QImage::Format_ARGB32_Premultiplied ); // iterate over all pixels changing the alpha component value unsigned int * data = (unsigned int *)grayImage.bits(); unsigned int pixels = grayImage.width() * grayImage.height(); int red = color.red(), green = color.green(), blue = color.blue(); int source, sourceSat, sourceAlpha; for( unsigned int i = 0; i < pixels; ++i ) { // optimize this loop keeping byte order into account source = data[i]; sourceSat = qRed( source ); int newR = qt_div_255( sourceSat * red ), newG = qt_div_255( sourceSat * green ), newB = qt_div_255( sourceSat * blue ); if ( (sourceAlpha = qAlpha( source )) == 255 ) { // use destAlpha data[i] = qRgba( newR, newG, newB, destAlpha ); } else { // use destAlpha * sourceAlpha product if ( destAlpha < 255 ) sourceAlpha = qt_div_255( destAlpha * sourceAlpha ); data[i] = qRgba( newR, newG, newB, sourceAlpha ); } } } } diff --git a/ui/pageviewannotator.cpp b/ui/pageviewannotator.cpp index 83bf0fb04..4fd71502b 100644 --- a/ui/pageviewannotator.cpp +++ b/ui/pageviewannotator.cpp @@ -1,1262 +1,1277 @@ /*************************************************************************** * 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 "pageviewannotator.h" // qt / kde includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // system includes #include #include // local includes #include "core/area.h" #include "core/document.h" #include "core/page.h" #include "core/annotations.h" #include "settings.h" #include "annotationtools.h" #include "guiutils.h" #include "pageview.h" #include "debug_ui.h" /** @short PickPointEngine */ class PickPointEngine : public AnnotatorEngine { public: PickPointEngine( const QDomElement & engineElement ) : AnnotatorEngine( engineElement ), clicked( false ), pixmap( nullptr ), xscale( 1.0 ), yscale( 1.0 ) { // parse engine specific attributes hoverIconName = engineElement.attribute( QStringLiteral("hoverIcon") ); iconName = m_annotElement.attribute( QStringLiteral("icon") ); if ( m_annotElement.attribute( QStringLiteral("type") ) == QLatin1String("Stamp") && !iconName.simplified().isEmpty() ) hoverIconName = iconName; center = QVariant( engineElement.attribute( QStringLiteral("center") ) ).toBool(); bool ok = true; size = engineElement.attribute( QStringLiteral("size"), QStringLiteral("32") ).toInt( &ok ); if ( !ok ) size = 32; m_block = QVariant( engineElement.attribute( QStringLiteral("block") ) ).toBool(); // create engine objects if ( !hoverIconName.simplified().isEmpty() ) pixmap = new QPixmap( GuiUtils::loadStamp( hoverIconName, QSize( size, size ) ) ); } ~PickPointEngine() override { delete pixmap; } QRect event( EventType type, Button button, double nX, double nY, double xScale, double yScale, const Okular::Page * page ) override { xscale=xScale; yscale=yScale; pagewidth = page->width(); pageheight = page->height(); // only proceed if pressing left button if ( button != Left ) return QRect(); // start operation on click if ( type == Press && clicked == false ) { clicked = true; startpoint.x=nX; startpoint.y=nY; } // repaint if moving while pressing else if ( type == Move && clicked == true ) { } // operation finished on release else if ( type == Release && clicked == true ) { m_creationCompleted = true; } else return QRect(); // update variables and extents (zoom invariant rect) point.x = nX; point.y = nY; if ( center ) { rect.left = nX - ( size / ( xScale * 2.0 ) ); rect.top = nY - ( size / ( yScale * 2.0 ) ); } else { rect.left = nX; rect.top = nY; } rect.right = rect.left + size; rect.bottom = rect.top + size; QRect boundrect = rect.geometry( (int)xScale, (int)yScale ).adjusted( 0, 0, 1, 1 ); if ( m_block ) { const Okular::NormalizedRect tmprect( qMin( startpoint.x, point.x ), qMin( startpoint.y, point.y ), qMax( startpoint.x, point.x ), qMax( startpoint.y, point.y ) ); boundrect |= tmprect.geometry( (int)xScale, (int)yScale ).adjusted( 0, 0, 1, 1 ); } return boundrect; } void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ ) override { if ( clicked ) { if ( m_block ) { const QPen origpen = painter->pen(); QPen pen = painter->pen(); pen.setStyle( Qt::DashLine ); painter->setPen( pen ); const Okular::NormalizedRect tmprect( qMin( startpoint.x, point.x ), qMin( startpoint.y, point.y ), qMax( startpoint.x, point.x ), qMax( startpoint.y, point.y ) ); const QRect realrect = tmprect.geometry( (int)xScale, (int)yScale ); painter->drawRect( realrect ); painter->setPen( origpen ); } if ( pixmap ) painter->drawPixmap( QPointF( rect.left * xScale, rect.top * yScale ), *pixmap ); } } + void addTextNote( Okular::Annotation * &ann, Okular::TextAnnotation::InplaceIntent inplaceIntent, const QString summary ) + { + //note dialog + const QString prompt = i18n( "Text of the new note:" ); + bool resok; + const QString note = QInputDialog::getMultiLineText(nullptr, i18n( "New Text Note" ), prompt, QString(), &resok); + if(resok) + { + //add note + Okular::TextAnnotation * ta = new Okular::TextAnnotation(); + ann = ta; + ta->setFlags( ta->flags() | Okular::Annotation::FixedRotation ); + ta->setContents( note ); + ta->setTextType( Okular::TextAnnotation::InPlace ); + ta->setInplaceIntent( inplaceIntent ); + //set alignment + if ( m_annotElement.hasAttribute( QStringLiteral("align") ) ) + ta->setInplaceAlignment( m_annotElement.attribute( QStringLiteral("align") ).toInt() ); + //set font + if ( m_annotElement.hasAttribute( QStringLiteral("font") ) ) + { + QFont f; + f.fromString( m_annotElement.attribute( QStringLiteral("font") ) ); + ta->setTextFont( f ); + } + //set width + if ( m_annotElement.hasAttribute( QStringLiteral ( "width" ) ) ) + { + ta->style().setWidth( m_annotElement.attribute( QStringLiteral ( "width" ) ).toDouble() ); + } + //set boundary + rect.left = qMin(startpoint.x,point.x); + rect.top = qMin(startpoint.y,point.y); + rect.right = qMax(startpoint.x,point.x); + rect.bottom = qMax(startpoint.y,point.y); + qCDebug(OkularUiDebug).nospace() << "xyScale=" << xscale << "," << yscale; + static const int padding = 2; + const QFontMetricsF mf(ta->textFont()); + const QRectF rcf = mf.boundingRect( Okular::NormalizedRect( rect.left, rect.top, 1.0, 1.0 ).geometry( (int)pagewidth, (int)pageheight ).adjusted( padding, padding, -padding, -padding ), + Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap, ta->contents() ); + rect.right = qMax(rect.right, rect.left+(rcf.width()+padding*2)/pagewidth); + rect.bottom = qMax(rect.bottom, rect.top+(rcf.height()+padding*2)/pageheight); + ta->window().setSummary( summary ); + } + } + QList< Okular::Annotation* > end() override { // find out annotation's description node if ( m_annotElement.isNull() ) { m_creationCompleted = false; clicked = false; return QList< Okular::Annotation* >(); } // find out annotation's type Okular::Annotation * ann = nullptr; const QString typeString = m_annotElement.attribute( QStringLiteral("type") ); // create TextAnnotation from path if ( typeString == QLatin1String("FreeText")) //setFlags( ta->flags() | Okular::Annotation::FixedRotation ); - ta->setContents( note ); - ta->setTextType( Okular::TextAnnotation::InPlace ); - //set alignment - if ( m_annotElement.hasAttribute( QStringLiteral("align") ) ) - ta->setInplaceAlignment( m_annotElement.attribute( QStringLiteral("align") ).toInt() ); - //set font - if ( m_annotElement.hasAttribute( QStringLiteral("font") ) ) - { - QFont f; - f.fromString( m_annotElement.attribute( QStringLiteral("font") ) ); - ta->setTextFont( f ); - } - //set width - if ( m_annotElement.hasAttribute( QStringLiteral ( "width" ) ) ) - { - ta->style().setWidth( m_annotElement.attribute( QStringLiteral ( "width" ) ).toDouble() ); - } - //set boundary - rect.left = qMin(startpoint.x,point.x); - rect.top = qMin(startpoint.y,point.y); - rect.right = qMax(startpoint.x,point.x); - rect.bottom = qMax(startpoint.y,point.y); - qCDebug(OkularUiDebug).nospace() << "xyScale=" << xscale << "," << yscale; - static const int padding = 2; - const QFontMetricsF mf(ta->textFont()); - const QRectF rcf = mf.boundingRect( Okular::NormalizedRect( rect.left, rect.top, 1.0, 1.0 ).geometry( (int)pagewidth, (int)pageheight ).adjusted( padding, padding, -padding, -padding ), - Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap, ta->contents() ); - rect.right = qMax(rect.right, rect.left+(rcf.width()+padding*2)/pagewidth); - rect.bottom = qMax(rect.bottom, rect.top+(rcf.height()+padding*2)/pageheight); - ta->window().setSummary( i18n( "Inline Note" ) ); - } - } + addTextNote( ann, Okular::TextAnnotation::Unknown, i18n("Inline Note") ); + else if ( typeString == QLatin1String("Typewriter")) + addTextNote( ann, Okular::TextAnnotation::TypeWriter, i18n("Typewriter") ); else if ( typeString == QLatin1String("Text")) { Okular::TextAnnotation * ta = new Okular::TextAnnotation(); ann = ta; ta->setTextType( Okular::TextAnnotation::Linked ); ta->setTextIcon( iconName ); //ta->window.flags &= ~(Okular::Annotation::Hidden); const double iconhei=0.03; rect.left = point.x; rect.top = point.y; rect.right=rect.left+iconhei; rect.bottom=rect.top+iconhei*xscale/yscale; ta->window().setSummary( i18n( "Pop-up Note" ) ); } // create StampAnnotation from path else if ( typeString == QLatin1String("Stamp") ) { Okular::StampAnnotation * sa = new Okular::StampAnnotation(); ann = sa; sa->setStampIconName( iconName ); // set boundary rect.left = qMin( startpoint.x, point.x ); rect.top = qMin( startpoint.y, point.y ); rect.right = qMax( startpoint.x, point.x ); rect.bottom = qMax( startpoint.y, point.y ); const QRectF rcf = rect.geometry( (int)xscale, (int)yscale ); const int ml = ( rcf.bottomRight() - rcf.topLeft() ).toPoint().manhattanLength(); if ( ml <= QApplication::startDragDistance() ) { const double stampxscale = size / xscale; const double stampyscale = size / yscale; if ( center ) { rect.left = point.x - stampxscale / 2; rect.top = point.y - stampyscale / 2; } else { rect.left = point.x; rect.top = point.y; } rect.right = rect.left + stampxscale; rect.bottom = rect.top + stampyscale; } } // create GeomAnnotation else if ( typeString == QLatin1String("GeomSquare") || typeString == QLatin1String("GeomCircle") ) { Okular::GeomAnnotation * ga = new Okular::GeomAnnotation(); ann = ga; // set the type if ( typeString == QLatin1String("GeomSquare") ) ga->setGeometricalType( Okular::GeomAnnotation::InscribedSquare ); else ga->setGeometricalType( Okular::GeomAnnotation::InscribedCircle ); if ( m_annotElement.hasAttribute( QStringLiteral("width") ) ) ann->style().setWidth( m_annotElement.attribute( QStringLiteral("width") ).toDouble() ); if ( m_annotElement.hasAttribute( QStringLiteral("innerColor") ) ) ga->setGeometricalInnerColor( QColor( m_annotElement.attribute( QStringLiteral("innerColor") ) ) ); //set boundary rect.left = qMin( startpoint.x, point.x ); rect.top = qMin( startpoint.y, point.y ); rect.right = qMax( startpoint.x, point.x ); rect.bottom = qMax( startpoint.y, point.y ); } m_creationCompleted = false; clicked = false; - // safety check if ( !ann ) return QList< Okular::Annotation* >(); // set common attributes ann->style().setColor( m_annotElement.hasAttribute( QStringLiteral("color") ) ? m_annotElement.attribute( QStringLiteral("color") ) : m_engineColor ); if ( m_annotElement.hasAttribute( QStringLiteral("opacity") ) ) ann->style().setOpacity( m_annotElement.attribute( QStringLiteral("opacity"), QStringLiteral("1.0") ).toDouble() ); // set the bounding rectangle, and make sure that the newly created // annotation lies within the page by translating it if necessary if ( rect.right > 1 ) { rect.left -= rect.right - 1; rect.right = 1; } if ( rect.bottom > 1 ) { rect.top -= rect.bottom - 1; rect.bottom = 1; } ann->setBoundingRectangle( rect ); // return annotation return QList< Okular::Annotation* >() << ann; } private: bool clicked; Okular::NormalizedRect rect; Okular::NormalizedPoint startpoint; Okular::NormalizedPoint point; QPixmap * pixmap; QString hoverIconName, iconName; int size; double xscale,yscale; double pagewidth, pageheight; bool center; bool m_block; }; /** @short PolyLineEngine */ class PolyLineEngine : public AnnotatorEngine { public: PolyLineEngine( const QDomElement & engineElement ) : AnnotatorEngine( engineElement ), last( false ) { // parse engine specific attributes m_block = engineElement.attribute( QStringLiteral("block") ) == QLatin1String("true"); bool ok = true; // numofpoints represents the max number of points for the current // polygon/polyline, with a pair of exceptions: // -1 means: the polyline must close on the first point (polygon) // 0 means: construct as many points as you want, right-click // to construct the last point numofpoints = engineElement.attribute( QStringLiteral("points") ).toInt( &ok ); if ( !ok ) numofpoints = -1; } QRect event( EventType type, Button button, double nX, double nY, double xScale, double yScale, const Okular::Page * /*page*/ ) override { // only proceed if pressing left button // if ( button != Left ) // return rect; // start operation if ( type == Press ) { newPoint.x = nX; newPoint.y = nY; if ( button == Right ) last = true; } // move the second point else if ( type == Move ) { movingpoint.x = nX; movingpoint.y = nY; const QRect oldmovingrect = movingrect; movingrect = rect | QRect( (int)( movingpoint.x * xScale ), (int)( movingpoint.y * yScale ), 1, 1 ); return oldmovingrect | movingrect; } else if ( type == Release ) { const Okular::NormalizedPoint tmppoint(nX, nY); if ( fabs( tmppoint.x - newPoint.x ) + fabs( tmppoint.y - newPoint.y ) > 1e-2 ) return rect; if ( numofpoints == -1 && points.count() > 1 && ( fabs( points[0].x - newPoint.x ) + fabs( points[0].y - newPoint.y ) < 1e-2 ) ) { last = true; } else { points.append( newPoint ); rect |= QRect( (int)( newPoint.x * xScale ), (int)( newPoint.y * yScale ), 1, 1 ); } // end creation if we have constructed the last point of enough points if ( last || points.count() == numofpoints ) { m_creationCompleted = true; last = false; normRect = Okular::NormalizedRect( rect, xScale, yScale ); } } return rect; } void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ ) override { if ( points.count() < 1 ) return; if ( m_block && points.count() == 2 ) { const Okular::NormalizedPoint first = points[0]; const Okular::NormalizedPoint second = points[1]; // draw a semitransparent block around the 2 points painter->setPen( m_engineColor ); painter->setBrush( QBrush( m_engineColor.light(), Qt::Dense4Pattern ) ); painter->drawRect( (int)(first.x * (double)xScale), (int)(first.y * (double)yScale), (int)((second.x - first.x) * (double)xScale), (int)((second.y - first.y) * (double)yScale) ); } else { // draw a polyline that connects the constructed points painter->setPen( QPen( m_engineColor, 2 ) ); for ( int i = 1; i < points.count(); ++i ) painter->drawLine( (int)(points[i - 1].x * (double)xScale), (int)(points[i - 1].y * (double)yScale), (int)(points[i].x * (double)xScale), (int)(points[i].y * (double)yScale) ); painter->drawLine( (int)(points.last().x * (double)xScale), (int)(points.last().y * (double)yScale), (int)(movingpoint.x * (double)xScale), (int)(movingpoint.y * (double)yScale) ); } } QList< Okular::Annotation* > end() override { m_creationCompleted = false; // find out annotation's description node if ( m_annotElement.isNull() ) return QList< Okular::Annotation* >(); // find out annotation's type Okular::Annotation * ann = nullptr; const QString typeString = m_annotElement.attribute( QStringLiteral("type") ); // create LineAnnotation from path if ( typeString == QLatin1String("Line") || typeString == QLatin1String("Polyline") || typeString == QLatin1String("Polygon") ) { if ( points.count() < 2 ) return QList< Okular::Annotation* >(); //add note Okular::LineAnnotation * la = new Okular::LineAnnotation(); ann = la; QLinkedList list; for ( int i = 0; i < points.count(); ++i ) list.append( points[ i ] ); la->setLinePoints( list ); if ( numofpoints == -1 ) { la->setLineClosed( true ); if ( m_annotElement.hasAttribute( QStringLiteral("innerColor") ) ) la->setLineInnerColor( QColor( m_annotElement.attribute( QStringLiteral("innerColor") ) ) ); } else if ( numofpoints == 2 ) { if ( m_annotElement.hasAttribute( QStringLiteral("leadFwd") ) ) la->setLineLeadingForwardPoint( m_annotElement.attribute( QStringLiteral("leadFwd") ).toDouble() ); if ( m_annotElement.hasAttribute( QStringLiteral("leadBack") ) ) la->setLineLeadingBackwardPoint( m_annotElement.attribute( QStringLiteral("leadBack") ).toDouble() ); } la->setBoundingRectangle( normRect ); } // safety check if ( !ann ) return QList< Okular::Annotation* >(); if ( m_annotElement.hasAttribute( QStringLiteral("width") ) ) ann->style().setWidth( m_annotElement.attribute( QStringLiteral("width") ).toDouble() ); // set common attributes ann->style().setColor( m_annotElement.hasAttribute( QStringLiteral("color") ) ? m_annotElement.attribute( QStringLiteral("color") ) : m_engineColor ); if ( m_annotElement.hasAttribute( QStringLiteral("opacity") ) ) ann->style().setOpacity( m_annotElement.attribute( QStringLiteral("opacity"), QStringLiteral("1.0") ).toDouble() ); // return annotation return QList< Okular::Annotation* >() << ann; } private: QList points; Okular::NormalizedPoint newPoint; Okular::NormalizedPoint movingpoint; QRect rect; QRect movingrect; Okular::NormalizedRect normRect; bool m_block; bool last; int numofpoints; }; /** @short TextSelectorEngine */ class TextSelectorEngine : public AnnotatorEngine { public: TextSelectorEngine( const QDomElement & engineElement, PageView * pageView ) : AnnotatorEngine( engineElement ), m_pageView( pageView ), selection( nullptr ) { // parse engine specific attributes } ~TextSelectorEngine() override { delete selection; } QRect event( EventType type, Button button, double nX, double nY, double xScale, double yScale, const Okular::Page * /*page*/ ) override { // only proceed if pressing left button if ( button != Left ) return QRect(); if ( type == Press ) { lastPoint.x = nX; lastPoint.y = nY; const QRect oldrect = rect; rect = QRect(); return oldrect; } else if ( type == Move ) { if ( item() ) { const QPoint start( (int)( lastPoint.x * item()->uncroppedWidth() ), (int)( lastPoint.y * item()->uncroppedHeight() ) ); const QPoint end( (int)( nX * item()->uncroppedWidth() ), (int)( nY * item()->uncroppedHeight() ) ); delete selection; selection = nullptr; Okular::RegularAreaRect * newselection = m_pageView->textSelectionForItem( item(), start, end ); if ( newselection && !newselection->isEmpty() ) { const QList geom = newselection->geometry( (int)xScale, (int)yScale ); QRect newrect; Q_FOREACH ( const QRect& r, geom ) { if ( newrect.isNull() ) newrect = r; else newrect |= r; } rect |= newrect; selection = newselection; } else { delete newselection; } } } else if ( type == Release && selection ) { m_creationCompleted = true; } return rect; } void paint( QPainter * painter, double xScale, double yScale, const QRect & /*clipRect*/ ) override { if ( selection ) { painter->setPen( Qt::NoPen ); QColor col = m_engineColor; col.setAlphaF( 0.5 ); painter->setBrush( col ); foreach( const Okular::NormalizedRect & r, *selection ) { painter->drawRect( r.geometry( (int)xScale, (int)yScale ) ); } } } QList< Okular::Annotation* > end() override { m_creationCompleted = false; // safety checks if ( m_annotElement.isNull() || !selection ) return QList< Okular::Annotation* >(); // find out annotation's type Okular::Annotation * ann = nullptr; const QString typeString = m_annotElement.attribute( QStringLiteral("type") ); Okular::HighlightAnnotation::HighlightType type = Okular::HighlightAnnotation::Highlight; bool typevalid = false; // create HighlightAnnotation's from the selected area if ( typeString == QLatin1String("Highlight") ) { type = Okular::HighlightAnnotation::Highlight; typevalid = true; } else if ( typeString == QLatin1String("Squiggly") ) { type = Okular::HighlightAnnotation::Squiggly; typevalid = true; } else if ( typeString == QLatin1String("Underline") ) { type = Okular::HighlightAnnotation::Underline; typevalid = true; } else if ( typeString == QLatin1String("StrikeOut") ) { type = Okular::HighlightAnnotation::StrikeOut; typevalid = true; } if ( typevalid ) { Okular::HighlightAnnotation * ha = new Okular::HighlightAnnotation(); ha->setHighlightType( type ); ha->setBoundingRectangle( Okular::NormalizedRect( rect, item()->uncroppedWidth(), item()->uncroppedHeight() ) ); foreach ( const Okular::NormalizedRect & r, *selection ) { Okular::HighlightAnnotation::Quad q; q.setCapStart( false ); q.setCapEnd( false ); q.setFeather( 1.0 ); q.setPoint( Okular::NormalizedPoint( r.left, r.bottom ), 0 ); q.setPoint( Okular::NormalizedPoint( r.right, r.bottom ), 1 ); q.setPoint( Okular::NormalizedPoint( r.right, r.top ), 2 ); q.setPoint( Okular::NormalizedPoint( r.left, r.top ), 3 ); ha->highlightQuads().append( q ); } ann = ha; } delete selection; selection = nullptr; // safety check if ( !ann ) return QList< Okular::Annotation* >(); // set common attributes ann->style().setColor( m_annotElement.hasAttribute( QStringLiteral("color") ) ? m_annotElement.attribute( QStringLiteral("color") ) : m_engineColor ); if ( m_annotElement.hasAttribute( QStringLiteral("opacity") ) ) ann->style().setOpacity( m_annotElement.attribute( QStringLiteral("opacity"), QStringLiteral("1.0") ).toDouble() ); // return annotations return QList< Okular::Annotation* >() << ann; } QCursor cursor() const override { return Qt::IBeamCursor; } private: // data PageView * m_pageView; // TODO: support more pages Okular::RegularAreaRect * selection; Okular::NormalizedPoint lastPoint; QRect rect; }; /** PageViewAnnotator **/ PageViewAnnotator::PageViewAnnotator( PageView * parent, Okular::Document * storage ) : QObject( parent ), m_document( storage ), m_pageView( parent ), m_toolBar( nullptr ), m_engine( nullptr ), m_textToolsEnabled( false ), m_toolsEnabled( false ), m_continuousMode( false ), m_hidingWasForced( false ), m_lastToolID( -1 ), m_lockedItem( nullptr ) { reparseConfig(); } void PageViewAnnotator::reparseConfig() { m_items.clear(); // Read tool list from configuration. It's a list of XML elements const QStringList userTools = Okular::Settings::annotationTools(); // Populate m_toolsDefinition QDomDocument doc; m_toolsDefinition = doc.createElement( QStringLiteral("annotatingTools") ); foreach ( const QString &toolXml, userTools ) { QDomDocument entryParser; if ( entryParser.setContent( toolXml ) ) m_toolsDefinition.appendChild( doc.importNode( entryParser.documentElement(), true ) ); else qCWarning(OkularUiDebug) << "Skipping malformed tool XML in AnnotationTools setting"; } // Create the AnnotationToolItems from the XML dom tree QDomNode toolDescription = m_toolsDefinition.firstChild(); while ( toolDescription.isElement() ) { QDomElement toolElement = toolDescription.toElement(); if ( toolElement.tagName() == QLatin1String("tool") ) { AnnotationToolItem item; item.id = toolElement.attribute(QStringLiteral("id")).toInt(); if ( toolElement.hasAttribute( QStringLiteral("name") ) ) item.text = toolElement.attribute( QStringLiteral("name") ); else item.text = defaultToolName( toolElement ); item.pixmap = makeToolPixmap( toolElement ); QDomNode shortcutNode = toolElement.elementsByTagName( QStringLiteral("shortcut") ).item( 0 ); if ( shortcutNode.isElement() ) item.shortcut = shortcutNode.toElement().text(); QDomNodeList engineNodeList = toolElement.elementsByTagName( QStringLiteral("engine") ); if ( engineNodeList.size() > 0 ) { QDomElement engineEl = engineNodeList.item( 0 ).toElement(); if ( !engineEl.isNull() && engineEl.hasAttribute( QStringLiteral("type") ) ) item.isText = engineEl.attribute( QStringLiteral("type") ) == QLatin1String( "TextSelector" ); } m_items.push_back( item ); } toolDescription = toolDescription.nextSibling(); } } PageViewAnnotator::~PageViewAnnotator() { delete m_engine; } void PageViewAnnotator::setEnabled( bool on ) { if ( !on ) { // remove toolBar if ( m_toolBar ) m_toolBar->hideAndDestroy(); m_toolBar = nullptr; // deactivate the active tool, if any slotToolSelected( -1 ); return; } // if no tools are defined, don't show the toolbar if ( !m_toolsDefinition.hasChildNodes() ) return; // create toolBar if ( !m_toolBar ) { m_toolBar = new PageViewToolBar( m_pageView, m_pageView->viewport() ); m_toolBar->setSide( (PageViewToolBar::Side)Okular::Settings::editToolBarPlacement() ); m_toolBar->setItems( m_items ); m_toolBar->setToolsEnabled( m_toolsEnabled ); m_toolBar->setTextToolsEnabled( m_textToolsEnabled ); connect(m_toolBar, &PageViewToolBar::toolSelected, this, &PageViewAnnotator::slotToolSelected); connect(m_toolBar, &PageViewToolBar::orientationChanged, this, &PageViewAnnotator::slotSaveToolbarOrientation); connect(m_toolBar, &PageViewToolBar::buttonDoubleClicked, this, &PageViewAnnotator::slotToolDoubleClicked); m_toolBar->setCursor(Qt::ArrowCursor); } // show the toolBar m_toolBar->showAndAnimate(); } void PageViewAnnotator::setTextToolsEnabled( bool enabled ) { m_textToolsEnabled = enabled; if ( m_toolBar ) m_toolBar->setTextToolsEnabled( m_textToolsEnabled ); } void PageViewAnnotator::setToolsEnabled( bool enabled ) { m_toolsEnabled = enabled; if ( m_toolBar ) m_toolBar->setToolsEnabled( m_toolsEnabled ); } void PageViewAnnotator::setHidingForced( bool forced ) { m_hidingWasForced = forced; } bool PageViewAnnotator::hidingWasForced() const { return m_hidingWasForced; } bool PageViewAnnotator::active() const { return m_engine && m_toolBar; } bool PageViewAnnotator::annotating() const { return active() && m_lockedItem; } QCursor PageViewAnnotator::cursor() const { return m_engine->cursor(); } QRect PageViewAnnotator::performRouteMouseOrTabletEvent(const AnnotatorEngine::EventType & eventType, const AnnotatorEngine::Button & button, const QPointF & pos, PageViewItem * item ) { // if the right mouse button was pressed, we simply do nothing. In this way, we are still editing the annotation // and so this function will receive and process the right mouse button release event too. If we detach now the annotation tool, // the release event will be processed by the PageView class which would create the annotation property widget, and we do not want this. if ( button == AnnotatorEngine::Right && eventType == AnnotatorEngine::Press ) return QRect(); else if ( button == AnnotatorEngine::Right && eventType == AnnotatorEngine::Release ) { detachAnnotation(); return QRect(); } // 1. lock engine to current item if ( !m_lockedItem && eventType == AnnotatorEngine::Press ) { m_lockedItem = item; m_engine->setItem( m_lockedItem ); } if ( !m_lockedItem ) { return QRect(); } // find out normalized mouse coords inside current item const QRect & itemRect = m_lockedItem->uncroppedGeometry(); const QPointF eventPos = m_pageView->contentAreaPoint( pos ); const double nX = qBound( 0.0, m_lockedItem->absToPageX( eventPos.x() ), 1.0 ); const double nY = qBound( 0.0, m_lockedItem->absToPageY( eventPos.y() ), 1.0 ); QRect modifiedRect; // 2. use engine to perform operations const QRect paintRect = m_engine->event( eventType, button, nX, nY, itemRect.width(), itemRect.height(), m_lockedItem->page() ); // 3. update absolute extents rect and send paint event(s) if ( paintRect.isValid() ) { // 3.1. unite old and new painting regions QRegion compoundRegion( m_lastDrawnRect ); m_lastDrawnRect = paintRect; m_lastDrawnRect.translate( itemRect.left(), itemRect.top() ); // 3.2. decompose paint region in rects and send paint events const QVector rects = compoundRegion.united( m_lastDrawnRect ).rects(); const QPoint areaPos = m_pageView->contentAreaPosition(); for ( int i = 0; i < rects.count(); i++ ) m_pageView->viewport()->update( rects[i].translated( -areaPos ) ); modifiedRect = compoundRegion.boundingRect() | m_lastDrawnRect; } // 4. if engine has finished, apply Annotation to the page if ( m_engine->creationCompleted() ) { // apply engine data to the Annotation's and reset engine QList< Okular::Annotation* > annotations = m_engine->end(); // attach the newly filled annotations to the page foreach ( Okular::Annotation * annotation, annotations ) { if ( !annotation ) continue; annotation->setCreationDate( QDateTime::currentDateTime() ); annotation->setModificationDate( QDateTime::currentDateTime() ); annotation->setAuthor( Okular::Settings::identityAuthor() ); m_document->addPageAnnotation( m_lockedItem->pageNumber(), annotation ); if ( annotation->openDialogAfterCreation() ) m_pageView->openAnnotationWindow( annotation, m_lockedItem->pageNumber() ); } if ( m_continuousMode ) slotToolSelected( m_lastToolID ); else detachAnnotation(); } return modifiedRect; } QRect PageViewAnnotator::routeMouseEvent( QMouseEvent * e, PageViewItem * item ) { AnnotatorEngine::EventType eventType; AnnotatorEngine::Button button; // figure out the event type and button AnnotatorEngine::decodeEvent( e, &eventType, &button ); return performRouteMouseOrTabletEvent( eventType, button, e->localPos(), item ); } QRect PageViewAnnotator::routeTabletEvent( QTabletEvent * e, PageViewItem * item, const QPoint & localOriginInGlobal ) { // Unlike routeMouseEvent, routeTabletEvent must explicitly ignore events it doesn't care about so that // the corresponding mouse event will later be delivered. if ( !item ) { e->ignore(); return QRect(); } // We set all tablet events that take place over the annotations toolbar to ignore so that corresponding mouse // events will be delivered to the toolbar. However, we still allow the annotations code to handle // TabletMove and TabletRelease events in case the user is drawing an annotation onto the toolbar. const QPoint toolBarPos = m_toolBar->mapFromGlobal( e->globalPos() ); const QRect toolBarRect = m_toolBar->rect(); if ( toolBarRect.contains( toolBarPos ) ) { e->ignore(); if (e->type() == QEvent::TabletPress) return QRect(); } AnnotatorEngine::EventType eventType; AnnotatorEngine::Button button; // figure out the event type and button AnnotatorEngine::decodeEvent( e, &eventType, &button ); const QPointF globalPosF = e->globalPosF(); const QPointF localPosF = globalPosF - localOriginInGlobal; return performRouteMouseOrTabletEvent( eventType, button, localPosF, item ); } bool PageViewAnnotator::routeKeyEvent( QKeyEvent * event ) { if ( event->key() == Qt::Key_Escape ) { detachAnnotation(); return true; } return false; } bool PageViewAnnotator::routePaints( const QRect & wantedRect ) const { return m_engine && m_toolBar && wantedRect.intersects( m_lastDrawnRect ) && m_lockedItem; } void PageViewAnnotator::routePaint( QPainter * painter, const QRect & paintRect ) { // if there's no locked item, then there's no decided place to draw on if ( !m_lockedItem ) return; #ifndef NDEBUG // [DEBUG] draw the paint region if enabled if ( Okular::Settings::debugDrawAnnotationRect() ) painter->drawRect( paintRect ); #endif // move painter to current itemGeometry rect const QRect & itemRect = m_lockedItem->uncroppedGeometry(); painter->save(); painter->translate( itemRect.topLeft() ); // TODO: Clip annotation painting to cropped page. // transform cliprect from absolute to item relative coords QRect annotRect = paintRect.intersected( m_lastDrawnRect ); annotRect.translate( -itemRect.topLeft() ); // use current engine for painting (in virtual page coordinates) m_engine->paint( painter, m_lockedItem->uncroppedWidth(), m_lockedItem->uncroppedHeight(), annotRect ); painter->restore(); } void PageViewAnnotator::slotToolSelected( int toolID ) { // terminate any previous operation if ( m_engine ) { delete m_engine; m_engine = nullptr; } m_lockedItem = nullptr; if ( m_lastDrawnRect.isValid() ) { m_pageView->viewport()->update( m_lastDrawnRect.translated( -m_pageView->contentAreaPosition() ) ); m_lastDrawnRect = QRect(); } if ( toolID != m_lastToolID ) m_continuousMode = false; // store current tool for later usage m_lastToolID = toolID; // handle tool deselection if ( toolID == -1 ) { m_pageView->displayMessage( QString() ); m_pageView->updateCursor(); return; } // for the selected tool create the Engine QDomNode toolNode = m_toolsDefinition.firstChild(); while ( toolNode.isElement() ) { QDomElement toolElement = toolNode.toElement(); toolNode = toolNode.nextSibling(); // only find out the element describing selected tool if ( toolElement.tagName() != QLatin1String("tool") || toolElement.attribute(QStringLiteral("id")).toInt() != toolID ) continue; // parse tool properties QDomNode toolSubNode = toolElement.firstChild(); while ( toolSubNode.isElement() ) { QDomElement toolSubElement = toolSubNode.toElement(); toolSubNode = toolSubNode.nextSibling(); // create the AnnotatorEngine if ( toolSubElement.tagName() == QLatin1String("engine") ) { QString type = toolSubElement.attribute( QStringLiteral("type") ); if ( type == QLatin1String("SmoothLine") ) m_engine = new SmoothPathEngine( toolSubElement ); else if ( type == QLatin1String("PickPoint") ) m_engine = new PickPointEngine( toolSubElement ); else if ( type == QLatin1String("PolyLine") ) m_engine = new PolyLineEngine( toolSubElement ); else if ( type == QLatin1String("TextSelector") ) m_engine = new TextSelectorEngine( toolSubElement, m_pageView ); else qCWarning(OkularUiDebug).nospace() << "tools.xml: engine type:'" << type << "' is not defined!"; } // display the tooltip const QString annotType = toolElement.attribute( QStringLiteral("type") ); QString tip; if ( annotType == QLatin1String("ellipse") ) tip = i18nc( "Annotation tool", "Draw an ellipse (drag to select a zone)" ); else if ( annotType == QLatin1String("highlight") ) tip = i18nc( "Annotation tool", "Highlight text" ); else if ( annotType == QLatin1String("ink") ) tip = i18nc( "Annotation tool", "Draw a freehand line" ); else if ( annotType == QLatin1String("note-inline") ) tip = i18nc( "Annotation tool", "Inline Text Annotation (drag to select a zone)" ); else if ( annotType == QLatin1String("note-linked") ) tip = i18nc( "Annotation tool", "Put a pop-up note" ); else if ( annotType == QLatin1String("polygon") ) tip = i18nc( "Annotation tool", "Draw a polygon (click on the first point to close it)" ); else if ( annotType == QLatin1String("rectangle") ) tip = i18nc( "Annotation tool", "Draw a rectangle" ); else if ( annotType == QLatin1String("squiggly") ) tip = i18nc( "Annotation tool", "Squiggle text" ); else if ( annotType == QLatin1String("stamp") ) tip = i18nc( "Annotation tool", "Put a stamp symbol" ); else if ( annotType == QLatin1String("straight-line") ) tip = i18nc( "Annotation tool", "Draw a straight line" ); else if ( annotType == QLatin1String("strikeout") ) tip = i18nc( "Annotation tool", "Strike out text" ); else if ( annotType == QLatin1String("underline") ) tip = i18nc( "Annotation tool", "Underline text" ); + else if ( annotType == QLatin1String("typewriter") ) + tip = i18nc( "Annotation tool", "Typewriter Annotation (drag to select a zone)" ); if ( !tip.isEmpty() && !m_continuousMode ) m_pageView->displayMessage( tip, QString(), PageViewMessage::Annotation ); } // consistancy warning if ( !m_engine ) { qCWarning(OkularUiDebug) << "tools.xml: couldn't find good engine description. check xml."; } m_pageView->updateCursor(); // stop after parsing selected tool's node break; } } void PageViewAnnotator::slotSaveToolbarOrientation( int side ) { Okular::Settings::setEditToolBarPlacement( (int)side ); Okular::Settings::self()->save(); } void PageViewAnnotator::slotToolDoubleClicked( int /*toolID*/ ) { m_continuousMode = true; } void PageViewAnnotator::detachAnnotation() { m_toolBar->selectButton( -1 ); } QString PageViewAnnotator::defaultToolName( const QDomElement &toolElement ) { const QString annotType = toolElement.attribute( QStringLiteral("type") ); if ( annotType == QLatin1String("ellipse") ) return i18n( "Ellipse" ); else if ( annotType == QLatin1String("highlight") ) return i18n( "Highlighter" ); else if ( annotType == QLatin1String("ink") ) return i18n( "Freehand Line" ); else if ( annotType == QLatin1String("note-inline") ) return i18n( "Inline Note" ); else if ( annotType == QLatin1String("note-linked") ) return i18n( "Pop-up Note" ); else if ( annotType == QLatin1String("polygon") ) return i18n( "Polygon" ); else if ( annotType == QLatin1String("rectangle") ) return i18n( "Rectangle" ); else if ( annotType == QLatin1String("squiggly") ) return i18n( "Squiggle" ); else if ( annotType == QLatin1String("stamp") ) return i18n( "Stamp" ); else if ( annotType == QLatin1String("straight-line") ) return i18n( "Straight Line" ); else if ( annotType == QLatin1String("strikeout") ) return i18n( "Strike out" ); else if ( annotType == QLatin1String("underline") ) return i18n( "Underline" ); + else if ( annotType == QLatin1String("typewriter") ) + return i18n( "Typewriter" ); else return QString(); } QPixmap PageViewAnnotator::makeToolPixmap( const QDomElement &toolElement ) { QPixmap pixmap( 32 * qApp->devicePixelRatio(), 32 * qApp->devicePixelRatio() ); pixmap.setDevicePixelRatio( qApp->devicePixelRatio() ); const QString annotType = toolElement.attribute( QStringLiteral("type") ); // Load HiDPI variant on HiDPI screen QString imageVariant; if ( qApp->devicePixelRatio() > 1.05 ) { imageVariant = "@2x"; } // Load base pixmap. We'll draw on top of it pixmap.load( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-base-okular" + imageVariant + ".png") ) ); /* Parse color, innerColor and icon (if present) */ QColor engineColor, innerColor; QString icon; QDomNodeList engineNodeList = toolElement.elementsByTagName( QStringLiteral("engine") ); if ( engineNodeList.size() > 0 ) { QDomElement engineEl = engineNodeList.item( 0 ).toElement(); if ( !engineEl.isNull() && engineEl.hasAttribute( QStringLiteral("color") ) ) engineColor = QColor( engineEl.attribute( QStringLiteral("color") ) ); } QDomNodeList annotationNodeList = toolElement.elementsByTagName( QStringLiteral("annotation") ); if ( annotationNodeList.size() > 0 ) { QDomElement annotationEl = annotationNodeList.item( 0 ).toElement(); if ( !annotationEl.isNull() && annotationEl.hasAttribute( QStringLiteral("innerColor") ) ) innerColor = QColor( annotationEl.attribute( QStringLiteral("innerColor") ) ); if ( !annotationEl.isNull() && annotationEl.hasAttribute( QStringLiteral("icon") ) ) icon = annotationEl.attribute( QStringLiteral("icon") ); } QPainter p( &pixmap ); if ( annotType == QLatin1String("ellipse") ) { p.setRenderHint( QPainter::Antialiasing ); if ( innerColor.isValid() ) p.setBrush( innerColor ); p.setPen( QPen( engineColor, 2 ) ); p.drawEllipse( 2, 7, 21, 14 ); } else if ( annotType == QLatin1String("highlight") ) { QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-highlighter-okular-colorizable" + imageVariant + ".png") ) ); QImage colorizedOverlay = overlay; GuiUtils::colorizeImage( colorizedOverlay, engineColor ); p.drawImage( QPoint(0,0), colorizedOverlay ); // Trail p.drawImage( QPoint(0,-32), overlay ); // Text + Shadow (uncolorized) p.drawImage( QPoint(0,-64), colorizedOverlay ); // Pen } else if ( annotType == QLatin1String("ink") ) { QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-ink-okular-colorizable" + imageVariant + ".png") ) ); QImage colorizedOverlay = overlay; GuiUtils::colorizeImage( colorizedOverlay, engineColor ); p.drawImage( QPoint(0,0), colorizedOverlay ); // Trail p.drawImage( QPoint(0,-32), overlay ); // Shadow (uncolorized) p.drawImage( QPoint(0,-64), colorizedOverlay ); // Pen } else if ( annotType == QLatin1String("note-inline") ) { QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-note-inline-okular-colorizable" + imageVariant + ".png") ) ); GuiUtils::colorizeImage( overlay, engineColor ); p.drawImage( QPoint(0,0), overlay ); } else if ( annotType == QLatin1String("note-linked") ) { QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-note-okular-colorizable" + imageVariant + ".png") ) ); GuiUtils::colorizeImage( overlay, engineColor ); p.drawImage( QPoint(0,0), overlay ); } else if ( annotType == QLatin1String("polygon") ) { QPainterPath path; path.moveTo( 0, 7 ); path.lineTo( 19, 7 ); path.lineTo( 19, 14 ); path.lineTo( 23, 14 ); path.lineTo( 23, 20 ); path.lineTo( 0, 20 ); if ( innerColor.isValid() ) p.setBrush( innerColor ); p.setPen( QPen( engineColor, 1 ) ); p.drawPath( path ); } else if ( annotType == QLatin1String("rectangle") ) { p.setRenderHint( QPainter::Antialiasing ); if ( innerColor.isValid() ) p.setBrush( innerColor ); p.setPen( QPen( engineColor, 2 ) ); p.drawRect( 2, 7, 21, 14 ); } else if ( annotType == QLatin1String("squiggly") ) { QPen pen( engineColor, 1 ); pen.setDashPattern( QVector() << 1 << 1 ); p.setPen( pen ); p.drawLine( 1, 13, 16, 13 ); p.drawLine( 2, 14, 15, 14 ); p.drawLine( 0, 20, 19, 20 ); p.drawLine( 1, 21, 18, 21 ); } else if ( annotType == QLatin1String("stamp") ) { QPixmap stamp = GuiUtils::loadStamp( icon, QSize( 16, 16 ) ); p.setRenderHint( QPainter::Antialiasing ); p.drawPixmap( 16, 14, stamp ); } else if ( annotType == QLatin1String("straight-line") ) { QPainterPath path; path.moveTo( 1, 8 ); path.lineTo( 20, 8 ); path.lineTo( 1, 27 ); path.lineTo( 20, 27 ); p.setRenderHint( QPainter::Antialiasing ); p.setPen( QPen( engineColor, 1 ) ); p.drawPath( path ); // TODO To be discussed: This is not a straight line! } else if ( annotType == QLatin1String("strikeout") ) { p.setPen( QPen( engineColor, 1 ) ); p.drawLine( 1, 10, 16, 10 ); p.drawLine( 0, 17, 19, 17 ); } else if ( annotType == QLatin1String("underline") ) { p.setPen( QPen( engineColor, 1 ) ); p.drawLine( 1, 13, 16, 13 ); p.drawLine( 0, 20, 19, 20 ); } + else if ( annotType == QLatin1String("typewriter") ) + { + QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-typewriter-okular-colorizable" + imageVariant + ".png") ) ); + GuiUtils::colorizeImage( overlay, engineColor ); + p.drawImage( QPoint(-2,2), overlay ); + } else { /* Unrecognized annotation type -- It shouldn't happen */ p.setPen( QPen( engineColor ) ); p.drawText( QPoint(20, 31), QStringLiteral("?") ); } return pixmap; } #include "moc_pageviewannotator.cpp" /* kate: replace-tabs on; indent-width 4; */