diff --git a/conf/editannottooldialog.cpp b/conf/editannottooldialog.cpp index 63f9ae9ee..b9eb8adc0 100644 --- a/conf/editannottooldialog.cpp +++ b/conf/editannottooldialog.cpp @@ -1,539 +1,542 @@ /*************************************************************************** * Copyright (C) 2012 by Fabio D'Urso * * Copyright (C) 2015 by Laurent Montel * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "editannottooldialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/annotations.h" #include "ui/annotationwidgets.h" #include "ui/pageviewannotator.h" EditAnnotToolDialog::EditAnnotToolDialog( QWidget *parent, const QDomElement &initialState ) : QDialog( parent ), m_stubann( nullptr ), m_annotationWidget( nullptr ) { QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &EditAnnotToolDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &EditAnnotToolDialog::reject); okButton->setDefault(true); QLabel * tmplabel; QWidget *widget = new QWidget( this ); QGridLayout * widgetLayout = new QGridLayout( widget ); mainLayout->addWidget(widget); mainLayout->addWidget(buttonBox); m_name = new KLineEdit( widget ); mainLayout->addWidget(m_name); tmplabel = new QLabel( i18n( "&Name:" ), widget ); mainLayout->addWidget(tmplabel); tmplabel->setBuddy( m_name ); widgetLayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); widgetLayout->addWidget( m_name, 0, 1 ); m_type = new KComboBox( false, widget ); mainLayout->addWidget(m_type); connect(m_type, static_cast(&KComboBox::currentIndexChanged), this, &EditAnnotToolDialog::slotTypeChanged); tmplabel = new QLabel( i18n( "&Type:" ), widget ); mainLayout->addWidget(tmplabel); tmplabel->setBuddy( m_type ); widgetLayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); widgetLayout->addWidget( m_type, 1, 1 ); m_toolIcon = new QLabel( widget ); mainLayout->addWidget(m_toolIcon); m_toolIcon->setAlignment( Qt::AlignRight | Qt::AlignTop ); m_toolIcon->setMinimumSize( 40, 32 ); widgetLayout->addWidget( m_toolIcon, 0, 2, 2, 1 ); m_appearanceBox = new QGroupBox( i18n( "Appearance" ), widget ); mainLayout->addWidget(m_appearanceBox); m_appearanceBox->setLayout( new QVBoxLayout( m_appearanceBox ) ); widgetLayout->addWidget( m_appearanceBox, 2, 0, 1, 3 ); // Populate combobox with annotation types m_type->addItem( i18n("Pop-up Note"), qVariantFromValue( ToolNoteLinked ) ); m_type->addItem( i18n("Inline Note"), qVariantFromValue( ToolNoteInline ) ); m_type->addItem( i18n("Freehand Line"), qVariantFromValue( ToolInk ) ); m_type->addItem( i18n("Straight Line"), qVariantFromValue( ToolStraightLine ) ); m_type->addItem( i18n("Polygon"), qVariantFromValue( ToolPolygon ) ); m_type->addItem( i18n("Text markup"), qVariantFromValue( ToolTextMarkup ) ); m_type->addItem( i18n("Geometrical shape"), qVariantFromValue( ToolGeometricalShape ) ); m_type->addItem( i18n("Stamp"), qVariantFromValue( ToolStamp ) ); 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_stubann; 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( QColor::HexArgb ); const QString textColor = static_cast< Okular::TextAnnotation * >( m_stubann )->textColor().name(); 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() ) ); } + annotationElement.setAttribute( QStringLiteral("endStyle"), QString::number( la->lineEndStyle() )); } 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("block"), QStringLiteral("true") ); annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Typewriter") ); annotationElement.setAttribute( QStringLiteral("color"), color ); annotationElement.setAttribute( QStringLiteral("textColor"), textColor ); 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) ); ta->setTextColor( Qt::black ); 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() ); + if ( annotationElement.hasAttribute( QStringLiteral("endStyle") ) ) + la->setLineEndStyle( (Okular::LineAnnotation::TermStyle)annotationElement.attribute( QStringLiteral("endStyle") ).toInt() ); } 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 ); } if ( annotationElement.hasAttribute( QStringLiteral("textColor") ) ) ta->setTextColor( QColor( annotationElement.attribute( QStringLiteral("textColor") ) ) ); } // 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/ui/annotationwidgets.cpp b/ui/annotationwidgets.cpp index 619f983f0..e20193fd9 100644 --- a/ui/annotationwidgets.cpp +++ b/ui/annotationwidgets.cpp @@ -1,848 +1,872 @@ /*************************************************************************** * 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 ) : m_ann( 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_colorBn) m_ann->style().setColor( m_colorBn->color() ); if (m_opacity) m_ann->style().setOpacity( (double)m_opacity->value() / 100.0 ); } QWidget * AnnotationWidget::createAppearanceWidget() { QWidget * widget = new QWidget(); QGridLayout * gridlayout = new QGridLayout( widget ); if ( hasColorButton() ) { 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 ); } if ( hasOpacityBox() ) { QLabel * 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 ); } QWidget * styleWidget = createStyleWidget(); if ( styleWidget ) gridlayout->addWidget( styleWidget, 2, 0, 1, 2 ); gridlayout->addItem( new QSpacerItem( 5, 5, QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ), 3, 0 ); if ( m_colorBn ) connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); if ( m_opacity ) 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_textAnn = static_cast< Okular::TextAnnotation * >( ann ); } QWidget * TextAnnotationWidget::createStyleWidget() { QWidget * widget = new QWidget(); QVBoxLayout * layout = new QVBoxLayout( widget ); layout->setMargin( 0 ); if ( m_textAnn->textType() == Okular::TextAnnotation::Linked ) { createPopupNoteStyleUi( widget, layout ); } else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace ) { if ( isTypewriter() ) createTypewriterStyleUi( widget, layout ); else createInlineNoteStyleUi( widget, layout ); } return widget; } bool TextAnnotationWidget::hasColorButton() const { return !isTypewriter(); } bool TextAnnotationWidget::hasOpacityBox() const { return !isTypewriter(); } void TextAnnotationWidget::applyChanges() { AnnotationWidget::applyChanges(); if ( m_textAnn->textType() == Okular::TextAnnotation::Linked ) { Q_ASSERT( m_pixmapSelector ); m_textAnn->setTextIcon( m_pixmapSelector->icon() ); } else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace ) { Q_ASSERT( m_fontReq ); m_textAnn->setTextFont( m_fontReq->font() ); if ( !isTypewriter() ) { Q_ASSERT( m_textAlign && m_spinWidth ); m_textAnn->setInplaceAlignment( m_textAlign->currentIndex() ); m_textAnn->style().setWidth( m_spinWidth->value() ); } else { Q_ASSERT( m_textColorBn ); m_textAnn->setTextColor( m_textColorBn->color() ); } } } void TextAnnotationWidget::createPopupNoteStyleUi( QWidget * widget, QVBoxLayout * layout ) { QGroupBox * gb = new QGroupBox( widget ); layout->addWidget( gb ); QHBoxLayout * gblay = new QHBoxLayout( gb ); gb->setTitle( i18n( "Icon" ) ); addPixmapSelector( gb, gblay ); } void TextAnnotationWidget::createInlineNoteStyleUi( QWidget * widget, QVBoxLayout * layout ) { QGridLayout * innerlay = new QGridLayout(); layout->addLayout( innerlay ); addFontRequester( widget, innerlay ); addTextAlignComboBox( widget, innerlay ); addWidthSpinBox( widget, innerlay ); } void TextAnnotationWidget::createTypewriterStyleUi( QWidget * widget, QVBoxLayout * layout ) { QGridLayout * innerlay = new QGridLayout(); layout->addLayout( innerlay ); addFontRequester( widget, innerlay ); addTextColorButton( widget, innerlay ); } void TextAnnotationWidget::addPixmapSelector( QWidget * widget, QLayout * layout ) { m_pixmapSelector = new PixmapPreviewSelector( widget ); layout->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 ); } void TextAnnotationWidget::addFontRequester( QWidget * widget, QGridLayout * layout ) { const int row = layout->rowCount(); QLabel * tmplabel = new QLabel( i18n( "Font:" ), widget ); layout->addWidget( tmplabel, row, 0 ); m_fontReq = new KFontRequester( widget ); layout->addWidget( m_fontReq, row, 1 ); m_fontReq->setFont( m_textAnn->textFont() ); connect( m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged ); } void TextAnnotationWidget::addTextColorButton( QWidget * widget, QGridLayout * layout ) { const int row = layout->rowCount(); QLabel * tmplabel = new QLabel( i18n( "&Text Color:" ), widget ); layout->addWidget( tmplabel, row, 0, Qt::AlignRight ); m_textColorBn = new KColorButton( widget ); m_textColorBn->setColor( m_textAnn->textColor() ); tmplabel->setBuddy( m_textColorBn ); layout->addWidget( m_textColorBn, row, 1 ); connect( m_textColorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); } void TextAnnotationWidget::addTextAlignComboBox( QWidget * widget, QGridLayout * layout ) { const int row = layout->rowCount(); QLabel * tmplabel = new QLabel( i18n( "Align:" ), widget ); layout->addWidget( tmplabel, row, 0 ); m_textAlign = new KComboBox( widget ); layout->addWidget( m_textAlign, row, 1 ); m_textAlign->addItem( i18n("Left") ); m_textAlign->addItem( i18n("Center") ); m_textAlign->addItem( i18n("Right") ); m_textAlign->setCurrentIndex( m_textAnn->inplaceAlignment() ); connect( m_textAlign, SIGNAL(currentIndexChanged(int)), this, SIGNAL(dataChanged()) ); } void TextAnnotationWidget::addWidthSpinBox( QWidget * widget, QGridLayout * layout ) { const int row = layout->rowCount(); QLabel * tmplabel = new QLabel( i18n( "Border Width:" ), widget ); layout->addWidget( tmplabel, row, 0, Qt::AlignRight ); m_spinWidth = new QDoubleSpinBox( widget ); layout->addWidget( m_spinWidth, row, 1 ); tmplabel->setBuddy( m_spinWidth ); m_spinWidth->setRange( 0, 100 ); m_spinWidth->setValue( m_textAnn->style().width() ); m_spinWidth->setSingleStep( 0.1 ); connect( m_spinWidth, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); } 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()) ); + //Line Term Styles + QLabel * tmplabel3 = new QLabel( i18n( "Line End:" ), widget ); + gridlay2->addWidget( tmplabel3, 1, 0, Qt::AlignRight ); + m_termStyleCombo = new KComboBox( widget ); + tmplabel3->setBuddy( m_termStyleCombo ); + gridlay2->addWidget( m_termStyleCombo ); + tmplabel3->setToolTip( i18n("Only for PDF documents")); + m_termStyleCombo->setToolTip( i18n("Only for PDF documents")); + + m_termStyleCombo->addItem( i18n( "Square" ) ); + m_termStyleCombo->addItem( i18n( "Circle" ) ); + m_termStyleCombo->addItem( i18n( "Diamond" ) ); + m_termStyleCombo->addItem( i18n( "Open Arrow" ) ); + m_termStyleCombo->addItem( i18n( "Closed Arrow" ) ); + m_termStyleCombo->addItem( i18n( "None" ) ); + m_termStyleCombo->addItem( i18n( "Butt" ) ); + m_termStyleCombo->addItem( i18n( "Right Open Arrow" ) ); + m_termStyleCombo->addItem( i18n( "Right Closed Arrow" ) ); + m_termStyleCombo->addItem( i18n( "Slash" ) ); + m_termStyleCombo->setCurrentIndex( m_lineAnn->lineEndStyle() ); + + connect( m_termStyleCombo, SIGNAL(currentIndexChanged(int)), 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() ); + m_lineAnn->setLineEndStyle( (Okular::LineAnnotation::TermStyle)m_termStyleCombo->currentIndex()); } 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 0f8f76fa0..155e73c27 100644 --- a/ui/annotationwidgets.h +++ b/ui/annotationwidgets.h @@ -1,279 +1,280 @@ /*************************************************************************** * 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: explicit 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(); private: virtual bool hasColorButton() const { return true; } virtual bool hasOpacityBox() const { return true; } Okular::Annotation * m_ann; QWidget * m_appearanceWidget { nullptr }; QWidget * m_extraWidget { nullptr }; KColorButton *m_colorBn { nullptr }; QSpinBox *m_opacity { nullptr }; }; class QVBoxLayout; class QGridLayout; class TextAnnotationWidget : public AnnotationWidget { Q_OBJECT public: explicit TextAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; protected: QWidget * createStyleWidget() override; private: virtual bool hasColorButton() const override; virtual bool hasOpacityBox() const override; void createPopupNoteStyleUi( QWidget * widget, QVBoxLayout * layout ); void createInlineNoteStyleUi( QWidget * widget, QVBoxLayout * layout ); void createTypewriterStyleUi( QWidget * widget, QVBoxLayout * layout ); void addPixmapSelector( QWidget * widget, QLayout * layout ); void addFontRequester( QWidget * widget, QGridLayout * layout ); void addTextColorButton( QWidget * widget, QGridLayout * layout ); void addTextAlignComboBox( QWidget * widget, QGridLayout * layout ); void addWidthSpinBox( QWidget * widget, QGridLayout * layout ); inline bool isTypewriter() const { return ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ); } Okular::TextAnnotation * m_textAnn; PixmapPreviewSelector * m_pixmapSelector { nullptr }; KFontRequester * m_fontReq { nullptr }; KColorButton *m_textColorBn { nullptr }; QComboBox * m_textAlign { nullptr }; QDoubleSpinBox * m_spinWidth { nullptr }; }; 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; + QComboBox * m_termStyleCombo; }; 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/pageviewannotator.cpp b/ui/pageviewannotator.cpp index 4faa16649..729674bde 100644 --- a/ui/pageviewannotator.cpp +++ b/ui/pageviewannotator.cpp @@ -1,1279 +1,1281 @@ /*************************************************************************** * 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 #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 ), 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 = GuiUtils::loadStamp( hoverIconName, QSize( size, size ) ); } 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.isNull() ) painter->drawPixmap( QPointF( rect.left * xScale, rect.top * yScale ), pixmap ); } } void addInPlaceTextAnnotation( Okular::Annotation * &ann, const QString summary, const QString content, Okular::TextAnnotation::InplaceIntent inplaceIntent ) { Okular::TextAnnotation * ta = new Okular::TextAnnotation(); ann = ta; ta->setFlags( ta->flags() | Okular::Annotation::FixedRotation ); ta->setContents( content ); 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 font color if ( m_annotElement.hasAttribute( QStringLiteral("textColor") ) ) { if ( inplaceIntent == Okular::TextAnnotation::TypeWriter ) ta->setTextColor( m_annotElement.attribute( QStringLiteral("textColor") ) ); else ta->setTextColor( Qt::black ); } //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 InPlace TextAnnotation from path if ( typeString == QLatin1String("FreeText") ) { bool resok; const QString content = QInputDialog::getMultiLineText(nullptr, i18n( "New Text Note" ), i18n( "Text of the new note:" ), QString(), &resok); if( resok ) addInPlaceTextAnnotation( ann, i18n("Inline Note"), content, Okular::TextAnnotation::Unknown ); } else if ( typeString == QLatin1String("Typewriter") ) { bool resok; const QString content = QInputDialog::getMultiLineText(nullptr, i18n( "New Text Note" ), i18n( "Text of the new note:" ), QString(), &resok); if( resok ) addInPlaceTextAnnotation( ann, i18n("Typewriter"), content, Okular::TextAnnotation::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.lighter(), 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() ); } + if ( m_annotElement.hasAttribute( QStringLiteral("endStyle") ) ) + la->setLineEndStyle( (Okular::LineAnnotation::TermStyle)m_annotElement.attribute( QStringLiteral("endStyle") ).toInt() ); 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 ) { // parse engine specific attributes } 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() ) ); selection.reset(); std::unique_ptr 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 = std::move(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; } selection.reset(); // 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 std::unique_ptr 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 ); } // consistency 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, textColor, annotColor; 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() ) { if ( annotationEl.hasAttribute( QStringLiteral("color") ) ) annotColor = annotationEl.attribute( QStringLiteral("color") ); if ( annotationEl.hasAttribute( QStringLiteral("innerColor") ) ) innerColor = QColor( annotationEl.attribute( QStringLiteral("innerColor") ) ); if ( annotationEl.hasAttribute( QStringLiteral("textColor") ) ) textColor = QColor( annotationEl.attribute( QStringLiteral("textColor") ) ); if ( 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, textColor ); 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; */