diff --git a/autotests/annotationstest.cpp b/autotests/annotationstest.cpp --- a/autotests/annotationstest.cpp +++ b/autotests/annotationstest.cpp @@ -30,6 +30,7 @@ // void testInk(); // void testHighlight(); // void testGeom(); + void testTypewriter(); void cleanupTestCase(); private: @@ -140,6 +141,29 @@ QTest::newRow("Highlight: Outside") << (Okular::Annotation*) highlight << 1.0 << 0.9 << qRound( pow( documentX * 0.1, 2 ) ); } +void AnnotationTest::testTypewriter() +{ + Okular::Annotation * annot = nullptr; + Okular::TextAnnotation * ta = new Okular::TextAnnotation(); + annot = ta; + ta->setFlags( ta->flags() | Okular::Annotation::FixedRotation ); + ta->setTextType( Okular::TextAnnotation::InPlace ); + ta->setInplaceIntent( Okular::TextAnnotation::TypeWriter ); + ta->style().setWidth( 0.0 ); + ta->style().setColor( QColor(255,255,255,0) ); + + annot->setBoundingRectangle( Okular::NormalizedRect( 0.8, 0.1, 0.85, 0.15 ) ); + annot->setContents( QStringLiteral("annot contents") ); + + m_document->addPageAnnotation( 0, annot ); + + QDomNode annotNode = annot->getAnnotationPropertiesDomNode(); + QDomNodeList annotNodeList = annotNode.toElement().elementsByTagName("base"); + QDomElement annotEl = annotNodeList.item(0).toElement(); + QCOMPARE( annotEl.attribute( QStringLiteral("color") ), QStringLiteral("#00ffffff") ); + QCOMPARE( annotEl.attribute( QStringLiteral("flags") ), QStringLiteral("4") ); + QCOMPARE( annotEl.attribute( QStringLiteral("contents") ), QStringLiteral("annot contents") ); +} QTEST_MAIN( AnnotationTest ) #include "annotationstest.moc" diff --git a/autotests/parttest.cpp b/autotests/parttest.cpp --- a/autotests/parttest.cpp +++ b/autotests/parttest.cpp @@ -41,7 +41,7 @@ Q_OBJECT public: - CloseDialogHelper(Okular::Part *p, QDialogButtonBox::StandardButton b) : m_part(p), m_button(b), m_clicked(false) + CloseDialogHelper(QDialogButtonBox::StandardButton b) : m_button(b), m_clicked(false) { QTimer::singleShot(0, this, &CloseDialogHelper::closeDialog); } @@ -54,18 +54,18 @@ private slots: void closeDialog() { - QDialog *dialog = m_part->widget()->findChild(); + QWidget *dialog = qApp->activeModalWidget(); if (!dialog) { QTimer::singleShot(0, this, &CloseDialogHelper::closeDialog); return; } QDialogButtonBox *buttonBox = dialog->findChild(); - buttonBox->button(m_button)->click(); + QPushButton *button = buttonBox->button(m_button); + QMetaObject::invokeMethod(button, "click", Qt::QueuedConnection); m_clicked = true; } private: - Okular::Part *m_part; QDialogButtonBox::StandardButton m_button; bool m_clicked; }; @@ -113,6 +113,7 @@ void testCrashTextEditDestroy(); void testAnnotWindow(); void testAdditionalActionTriggers(); + void testTypewriterAnnotTool(); private: void simulateMouseSelection(double startX, double startY, double endX, double endY, QWidget *target); @@ -844,7 +845,7 @@ { if ( !nativelySupportsAnnotations ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( nativeDirectSave.fileName() ), Part::NoSaveAsFlags ) ); // For backends that don't support annotations natively we mark the part as still modified @@ -857,12 +858,12 @@ { // We need to save to archive first otherwise we lose the annotation - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::Yes )); // this is the "you're going to lose the undo/redo stack" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::Yes )); // this is the "you're going to lose the undo/redo stack" dialog QVERIFY( part.saveAs( QUrl::fromLocalFile( archiveSave.fileName() ), Part::SaveAsOkularArchive ) ); if ( !nativelySupportsAnnotations ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( nativeDirectSave.fileName() ), Part::NoSaveAsFlags ) ); } @@ -880,16 +881,16 @@ if ( !nativelySupportsAnnotations ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( nativeFromArchiveFile.fileName() ), Part::NoSaveAsFlags ) ); if ( canSwapBackingFile && !nativelySupportsAnnotations ) { // For backends that don't support annotations natively we mark the part as still modified // after a save because we keep the annotation around but it will get lost if the user closes the app // so we want to give her a last chance to save on close with the "you have changes dialog" - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "do you want to save or discard" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "do you want to save or discard" dialog } part.closeUrl(); @@ -970,7 +971,7 @@ QString annotName = annot->uniqueName(); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile1.fileName() ), saveFlags ) ); @@ -1017,7 +1018,7 @@ // Check we can still undo the annot add after save if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile2.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canUndo() ); @@ -1048,31 +1049,31 @@ // Now check we can still undo/redo/save at all the intermediate states and things still work if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile2.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canUndo() ); part.m_document->undo(); QVERIFY( part.m_document->canUndo() ); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile1.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canUndo() ); part.m_document->undo(); QVERIFY( part.m_document->canUndo() ); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile2.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canUndo() ); part.m_document->undo(); QVERIFY( part.m_document->canUndo() ); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile1.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canUndo() ); @@ -1087,30 +1088,30 @@ QVERIFY( part.m_document->canRedo() ); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile2.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canRedo() ); part.m_document->redo(); QVERIFY( part.m_document->canRedo() ); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile1.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canRedo() ); part.m_document->redo(); QVERIFY( part.m_document->canRedo() ); if ( !nativelySupportsAnnotations && !saveToArchive ) { - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "you're going to lose the annotations" dialog } QVERIFY( part.saveAs( QUrl::fromLocalFile( saveFile2.fileName() ), saveFlags ) ); QVERIFY( part.m_document->canRedo() ); part.m_document->redo(); QVERIFY( !part.m_document->canRedo() ); - closeDialogHelper.reset(new CloseDialogHelper( &part, QDialogButtonBox::No )); // this is the "do you want to save or discard" dialog + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::No )); // this is the "do you want to save or discard" dialog part.closeUrl(); } @@ -1639,6 +1640,46 @@ verifyTargetStates( QStringLiteral( "pb" ), fields, false, false, false, __LINE__ ); } +void PartTest::testTypewriterAnnotTool() +{ + QScopedPointer closeDialogHelper; + Okular::Part part(nullptr, nullptr, QVariantList()); + + part.openUrl(QUrl::fromLocalFile(QStringLiteral(KDESRCDIR "data/file1.pdf"))); + + part.widget()->show(); + QVERIFY(QTest::qWaitForWindowExposed(part.widget())); + + const int width = part.m_pageView->horizontalScrollBar()->maximum() + + part.m_pageView->viewport()->width(); + const int height = part.m_pageView->verticalScrollBar()->maximum() + + part.m_pageView->viewport()->height(); + + part.m_document->setViewportPage(0); + + QMetaObject::invokeMethod(part.m_pageView, "slotToggleAnnotator", Q_ARG( bool, true )); + + QList toolbuttonList = part.m_pageView->findChildren(); + // Check if the tooltip of 10th button is "Typewriter" + QToolButton* typewriterButton = toolbuttonList.at(9); + QCOMPARE( typewriterButton->toolTip(), QStringLiteral("Typewriter") ); + + typewriterButton->click(); + + QTest::mouseMove(part.m_pageView->viewport(), QPoint(width * 0.5, height * 0.2)); + closeDialogHelper.reset(new CloseDialogHelper( QDialogButtonBox::Ok )); // this is the "add new note" dialog + QTest::mouseClick(part.m_pageView->viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(width * 0.5, height * 0.2)); + + Annotation* annot = part.m_document->page(0)->annotations().first(); + TextAnnotation* ta = static_cast( annot ); + QVERIFY( annot ); + QVERIFY( ta ); + QCOMPARE( annot->subType(), Okular::Annotation::AText ); + QCOMPARE( annot->style().color(), QColor(255,255,255,0) ); + QCOMPARE( ta->textType(), Okular::TextAnnotation::InPlace ); + QCOMPARE( ta->inplaceIntent(), Okular::TextAnnotation::TypeWriter ); +} + } // namespace Okular int main(int argc, char *argv[]) diff --git a/conf/editannottooldialog.h b/conf/editannottooldialog.h --- a/conf/editannottooldialog.h +++ b/conf/editannottooldialog.h @@ -40,7 +40,8 @@ ToolPolygon, ToolTextMarkup, ToolGeometricalShape, - ToolStamp + ToolStamp, + ToolTypewriter }; EditAnnotToolDialog( QWidget *parent = nullptr, const QDomElement &initialState = QDomElement() ); diff --git a/conf/editannottooldialog.cpp b/conf/editannottooldialog.cpp --- a/conf/editannottooldialog.cpp +++ b/conf/editannottooldialog.cpp @@ -85,6 +85,7 @@ 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(); @@ -124,7 +125,8 @@ toolElement.appendChild( engineElement ); engineElement.appendChild( annotationElement ); - const QString color = m_stubann->style().color().name(); + const QString color = m_stubann->style().color().name(QColor::HexArgb); + const QString fontColor = 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() ); @@ -258,6 +260,19 @@ annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Stamp") ); annotationElement.setAttribute( QStringLiteral("icon"), sa->stampIconName() ); } + else if ( toolType == ToolTypewriter ) + { + Okular::TextAnnotation * ta = static_cast( m_stubann ); + toolElement.setAttribute( QStringLiteral("type"), QStringLiteral("typewriter") ); + engineElement.setAttribute( QStringLiteral("type"), QStringLiteral("PickPoint") ); + engineElement.setAttribute( QStringLiteral("color"), fontColor ); + engineElement.setAttribute( QStringLiteral("block"), QStringLiteral("true") ); + annotationElement.setAttribute( QStringLiteral("type"), QStringLiteral("Typewriter") ); + annotationElement.setAttribute( QStringLiteral("color"), color ); + annotationElement.setAttribute( QStringLiteral("width"), width ); + if ( ta->textFont() != QApplication::font() ) + annotationElement.setAttribute( QStringLiteral("font"), ta->textFont().toString() ); + } if ( opacity != QStringLiteral("1") ) annotationElement.setAttribute( QStringLiteral("opacity"), opacity ); @@ -334,6 +349,16 @@ 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() @@ -464,6 +489,19 @@ 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 ( engineElement.hasAttribute( QStringLiteral("color") ) ) + ta->setTextColor( QColor( engineElement.attribute( QStringLiteral("color") ) ) ); + } // Common properties if ( annotationElement.hasAttribute( QStringLiteral("color") ) ) diff --git a/core/annotations.h b/core/annotations.h --- a/core/annotations.h +++ b/core/annotations.h @@ -819,6 +819,16 @@ */ QFont textFont() const; + /** + * Sets the @p color of the text annotation. + */ + void setTextColor( const QColor &color ); + + /** + * Returns the font color of the text annotation. + */ + QColor textColor() const; + /** * Sets the inplace @p alignment of the text annotation. */ diff --git a/core/annotations.cpp b/core/annotations.cpp --- a/core/annotations.cpp +++ b/core/annotations.cpp @@ -750,7 +750,7 @@ if ( d->m_flags ) // Strip internal flags e.setAttribute( QStringLiteral("flags"), d->m_flags & ~(External | ExternallyDrawn | BeingMoved | BeingResized ) ); if ( d->m_style.color().isValid() ) - e.setAttribute( QStringLiteral("color"), d->m_style.color().name() ); + e.setAttribute( QStringLiteral("color"), d->m_style.color().name(QColor::HexArgb) ); if ( d->m_style.opacity() != 1.0 ) e.setAttribute( QStringLiteral("opacity"), QString::number( d->m_style.opacity() ) ); @@ -1028,6 +1028,7 @@ TextAnnotation::TextType m_textType; QString m_textIcon; QFont m_textFont; + QColor m_textColor; int m_inplaceAlign; NormalizedPoint m_inplaceCallout[3]; NormalizedPoint m_transformedInplaceCallout[3]; @@ -1087,6 +1088,18 @@ return d->m_textFont; } +void TextAnnotation::setTextColor( const QColor &color ) +{ + Q_D( TextAnnotation ); + d->m_textColor = color; +} + +QColor TextAnnotation::textColor() const +{ + Q_D( const TextAnnotation ); + return d->m_textColor; +} + void TextAnnotation::setInplaceAlignment( int alignment ) { Q_D( TextAnnotation ); @@ -1160,6 +1173,8 @@ textElement.setAttribute( QStringLiteral("icon"), d->m_textIcon ); if ( d->m_textFont != QApplication::font() ) textElement.setAttribute( QStringLiteral("font"), d->m_textFont.toString() ); + if ( d->m_textColor.isValid() ) + textElement.setAttribute( QStringLiteral("fontColor"), d->m_textColor.name() ); if ( d->m_inplaceAlign ) textElement.setAttribute( QStringLiteral("align"), d->m_inplaceAlign ); if ( d->m_inplaceIntent != Unknown ) @@ -1246,6 +1261,8 @@ m_textIcon = e.attribute( QStringLiteral("icon") ); if ( e.hasAttribute( QStringLiteral("font") ) ) m_textFont.fromString( e.attribute( QStringLiteral("font") ) ); + if ( e.hasAttribute( QStringLiteral("fontColor") ) ) + m_textColor = QColor( e.attribute( QStringLiteral("fontColor") ) ); if ( e.hasAttribute( QStringLiteral("align") ) ) m_inplaceAlign = e.attribute( QStringLiteral("align") ).toInt(); if ( e.hasAttribute( QStringLiteral("intent") ) ) diff --git a/generators/poppler/CMakeLists.txt b/generators/poppler/CMakeLists.txt --- a/generators/poppler/CMakeLists.txt +++ b/generators/poppler/CMakeLists.txt @@ -96,6 +96,17 @@ } " HAVE_POPPLER_0_65) +check_cxx_source_compiles(" +#include +#include +int main() +{ + Poppler::TextAnnotation *annot = new Poppler::TextAnnotation( Poppler::TextAnnotation::InPlace ); + annot->setTextColor( Qt::blue ); + return 0; +} +" HAVE_POPPLER_0_67) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config-okular-poppler.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-okular-poppler.h diff --git a/generators/poppler/annots.cpp b/generators/poppler/annots.cpp --- a/generators/poppler/annots.cpp +++ b/generators/poppler/annots.cpp @@ -179,6 +179,9 @@ Poppler::TextAnnotation * ppl_txtann = static_cast(ppl_ann); ppl_txtann->setTextIcon( okl_txtann->textIcon() ); ppl_txtann->setTextFont( okl_txtann->textFont() ); + #ifdef HAVE_POPPLER_0_67 + ppl_txtann->setTextColor( okl_txtann->textColor() ); + #endif //HAVE_POPPLER_0_67 ppl_txtann->setInplaceAlign( okl_txtann->inplaceAlignment() ); ppl_txtann->setCalloutPoints( QVector() ); ppl_txtann->setInplaceIntent( (Poppler::TextAnnotation::InplaceIntent)okl_txtann->inplaceIntent() ); diff --git a/generators/poppler/config-okular-poppler.h.cmake b/generators/poppler/config-okular-poppler.h.cmake --- a/generators/poppler/config-okular-poppler.h.cmake +++ b/generators/poppler/config-okular-poppler.h.cmake @@ -30,3 +30,6 @@ /* Defined if we have the 0.65 version of the Poppler library */ #cmakedefine HAVE_POPPLER_0_65 1 + +/* Defined if we have the 0.67 version of the Poppler library */ +#cmakedefine HAVE_POPPLER_0_67 1 diff --git a/ui/annotationpropertiesdialog.cpp b/ui/annotationpropertiesdialog.cpp --- a/ui/annotationpropertiesdialog.cpp +++ b/ui/annotationpropertiesdialog.cpp @@ -117,7 +117,12 @@ if(((Okular::TextAnnotation*)m_annot)->textType()==Okular::TextAnnotation::Linked) captiontext = i18n( "Pop-up Note Properties" ); else - captiontext = i18n( "Inline Note Properties" ); + { + if(((Okular::TextAnnotation*)m_annot)->inplaceIntent()==Okular::TextAnnotation::TypeWriter) + captiontext = i18n( "Typewriter Properties" ); + else + captiontext = i18n( "Inline Note Properties" ); + } break; case Okular::Annotation::ALine: if ( ((Okular::LineAnnotation*)m_annot)->linePoints().count() == 2 ) diff --git a/ui/annotationwidgets.h b/ui/annotationwidgets.h --- a/ui/annotationwidgets.h +++ b/ui/annotationwidgets.h @@ -92,6 +92,7 @@ virtual QWidget * createExtraWidget(); Okular::Annotation * m_ann; + Okular::TextAnnotation * m_textAnn; QWidget * m_appearanceWidget; QWidget * m_extraWidget; KColorButton *m_colorBn; diff --git a/ui/annotationwidgets.cpp b/ui/annotationwidgets.cpp --- a/ui/annotationwidgets.cpp +++ b/ui/annotationwidgets.cpp @@ -159,6 +159,7 @@ AnnotationWidget::AnnotationWidget( Okular::Annotation * ann ) : QObject(), m_ann( ann ), m_appearanceWidget( nullptr ), m_extraWidget( nullptr ) { + m_textAnn = static_cast< Okular::TextAnnotation * >( ann ); } AnnotationWidget::~AnnotationWidget() @@ -190,40 +191,57 @@ void AnnotationWidget::applyChanges() { + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + { + m_textAnn->setTextColor( m_colorBn->color() ); + return; + } m_ann->style().setColor( m_colorBn->color() ); m_ann->style().setOpacity( (double)m_opacity->value() / 100.0 ); } QWidget * AnnotationWidget::createAppearanceWidget() { QWidget * widget = new QWidget(); QGridLayout * gridlayout = new QGridLayout( widget ); - - QLabel * tmplabel = new QLabel( i18n( "&Color:" ), widget ); - gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); - m_colorBn = new KColorButton( widget ); - m_colorBn->setColor( m_ann->style().color() ); - tmplabel->setBuddy( m_colorBn ); - gridlayout->addWidget( m_colorBn, 0, 1 ); - - tmplabel = new QLabel( i18n( "&Opacity:" ), widget ); - gridlayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); - m_opacity = new QSpinBox( widget ); - m_opacity->setRange( 0, 100 ); - m_opacity->setValue( (int)( m_ann->style().opacity() * 100 ) ); - m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80 %'", " %" ) ); - tmplabel->setBuddy( m_opacity ); - gridlayout->addWidget( m_opacity, 1, 1 ); - + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::Unknown ) + { + QLabel * tmplabel = new QLabel( i18n( "&Color:" ), widget ); + gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); + m_colorBn = new KColorButton( widget ); + m_colorBn->setColor( m_ann->style().color() ); + tmplabel->setBuddy( m_colorBn ); + gridlayout->addWidget( m_colorBn, 0, 1 ); + + tmplabel = new QLabel( i18n( "&Opacity:" ), widget ); + gridlayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); + m_opacity = new QSpinBox( widget ); + m_opacity->setRange( 0, 100 ); + m_opacity->setValue( (int)( m_ann->style().opacity() * 100 ) ); + m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80 %'", " %" ) ); + tmplabel->setBuddy( m_opacity ); + gridlayout->addWidget( m_opacity, 1, 1 ); + + connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); + connect( m_opacity, SIGNAL(valueChanged(int)), this, SIGNAL(dataChanged()) ); + } + else if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + { + QLabel * tmplabel = new QLabel( i18n( "&Font Color:" ), widget ); + gridlayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); + m_colorBn = new KColorButton( widget ); + m_colorBn->setColor( m_textAnn->textColor() ); + tmplabel->setBuddy( m_colorBn ); + gridlayout->addWidget( m_colorBn, 0, 1 ); + + connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); + } QWidget * styleWidget = createStyleWidget(); if ( styleWidget ) gridlayout->addWidget( styleWidget, 2, 0, 1, 2 ); gridlayout->addItem( new QSpacerItem( 5, 5, QSizePolicy::Fixed, QSizePolicy::MinimumExpanding ), 3, 0 ); - connect( m_colorBn, &KColorButton::changed, this, &AnnotationWidget::dataChanged ); - connect( m_opacity, SIGNAL(valueChanged(int)), this, SIGNAL(dataChanged()) ); - return widget; } @@ -281,6 +299,10 @@ innerlay->addWidget( m_fontReq, 0, 1 ); m_fontReq->setFont( m_textAnn->textFont() ); + connect( m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged ); + + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + return widget; tmplabel = new QLabel( i18n( "Align:" ), widget ); innerlay->addWidget( tmplabel, 1, 0 ); m_textAlign = new KComboBox( widget ); @@ -299,8 +321,6 @@ m_spinWidth->setValue( m_textAnn->style().width() ); m_spinWidth->setSingleStep( 0.1 ); - connect( m_fontReq, &KFontRequester::fontSelected, this, &AnnotationWidget::dataChanged ); - connect( m_textAlign, SIGNAL(currentIndexChanged(int)), this, SIGNAL(dataChanged()) ); connect( m_spinWidth, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); } @@ -318,6 +338,8 @@ else if ( m_textAnn->textType() == Okular::TextAnnotation::InPlace ) { m_textAnn->setTextFont( m_fontReq->font() ); + if ( m_textAnn->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + return; m_textAnn->setInplaceAlignment( m_textAlign->currentIndex() ); m_textAnn->style().setWidth( m_spinWidth->value() ); } diff --git a/ui/annotwindow.cpp b/ui/annotwindow.cpp --- a/ui/annotwindow.cpp +++ b/ui/annotwindow.cpp @@ -259,7 +259,7 @@ void AnnotWindow::reloadInfo() { - const QColor newcolor = m_annot->style().color().isValid() ? m_annot->style().color() : Qt::yellow; + const QColor newcolor = m_annot->style().color().isValid() ? QColor(m_annot->style().color().red(), m_annot->style().color().green(), m_annot->style().color().blue(), 255) : Qt::yellow; if ( newcolor != m_color ) { m_color = newcolor; diff --git a/ui/data/CMakeLists.txt b/ui/data/CMakeLists.txt --- a/ui/data/CMakeLists.txt +++ b/ui/data/CMakeLists.txt @@ -21,6 +21,8 @@ tool-note-inline.png tool-note-inline-okular-colorizable.png tool-note-inline-okular-colorizable@2x.png + tool-typewriter-okular-colorizable.png + tool-typewriter-okular-colorizable@2x.png DESTINATION ${KDE_INSTALL_DATADIR}/okular/pics) # install annotation page images install(FILES diff --git a/ui/data/sources/tool-typewriter-okular-colorizable.svgz b/ui/data/sources/tool-typewriter-okular-colorizable.svgz new file mode 100644 index 0000000000000000000000000000000000000000..8bb05eba3a075fd148a425ee5ffe712fce00a292 GIT binary patch literal 2611 zc$@(<3e5E%iwFP!000000M%J*Z{x-l{qA4EDqj**9M1cp*iM06Y!jdh6iu1}{Swd; zZL=$p0!7)<{`#Ifd`PmjN!#qAkb#w{J9lR8K>0Rn4$)?S#MZT=7;$~9SlkaY?uK$px$(yFg+hUPCm+fQnL-k=Z&(}rr z^<&$vXIb|A{LIQ;$m-@H`zA@#+pDYV&C|nGk|emd+RPU7n@PW6y=|6JaWT(|Ww9!% zc9RJvvdKYxeo&v&?d4N3uUD(O+QgmJ=Bp#+W^q4ilQqvODv1z6#$_gD3XOELuiAW< zo^FMW=eA1Dd4{!v=6|L&+aRE8{2SE=0o&BuW?tN5U%{%P&EEd>c3ez3TeOR#V}^L1 z+}DWGeZ3TYaeN=jxlNb^@uBBNz3Yth|}Db-hg6{knK=%C>0I`ongaH|e}y)=l{_ zzgrdzHhnz3wcfVtZTm~HYm2J80B#?}ATHBBcH;lIzu2worcLk5WzkuYJ=UutTi|=K z&N>saZJ90dHqUbQ4} zpstHK!M@Wz3M-j;GdWdN9e9$Hw5@;HA!o@fu^J!Ztj5065I{11@v}M~BYb}4oZf^K zv3e+xsHmqUv-IOm_rU8T=cIR&B#V*BC>Zf+^px_pcq)tML&Wa#P0=5=&L4`Hpqt58 z_wlD+xT~8*(F{w`2z#!rqEGot$m4FdcpmiC`sTIIWPRemhsxOhcHa{Cy)f5!KBO}+YSQ*dp6EZP}v08s(+qEDh^j0-2ql!L>e-LXy4HQ=*T$YQ=KX;b!NTP~MB z&?UXcM~9Z}vN#B@v)=mNx9rHd>um5oetUREN>=%LH%1U>#;Z~`h~vEf+EIEYm>vYnvMO-%>~7n($FhH+tjt!_dI#xv{UC^|OYpRvjmIr=l;Wnz z_tA(Ey72csTAH6qp1esUr{71Pr_Bgn8NuyAI2P2pg0XFlF<{#~!);Gf!WDSCN3)Cy&XwR7rWEP7&XtSLs=|F0+ihPoyTV-XscK zvpwUw&-3+CUMkfD?>!CC(~f!F=Xp=nWzz3`qWLt~d!QejrmQA)f-E{*1+<|8vy0^yT%7Oy?PA)>$d7^8f1#j;14w zFw+oNa01V94q=uXEs|woSP(XNF-UsWG(HM(s|!gg7&l%hMR#a!xzaqQ zGAL-P=`dw+aFQyfwRXWCMBii7VcrSlEP|&r<5D{`6i6N15sHQ&#iZiGhw;gP8UoKS z0%sha9?1|Lyw?aJ9tBrCOgY&ijFFZ?Vge6}ObK2Ggcm1zu3a=v=@1-Z$#}p$K1n^( zmV3&VWg5(*nBzLjkpKkU`Qcig=(2CIA4b5Xh&GLe0E(R>&AUftZCTG+bF1 z(`Xb26sMd?t`Pb@vxq6;9<0Z8Q49GJQ%W_LjyqtiM~|K!z+(uN+Q}(F4A$FM5=2ii zWsT={c#z11Rm@w?jTw=h{PPu2{pW1z7sgXzlmdp70AhpGCh`<4(cqeJ$e}WTCbn<~ z@>9qps6q-EQljAOmqwirAx%!Dukd631Md}XPhdWGel8g zQ4u3WiH9B(Rj!LSlr&6nk)vRyw*McFe#Q~?k~sQvVl|$nLm#9+dJro?MMsScO4_2i zMFyhA$|zHSlkVFmw((eIwG<|jQd8qqgd8+TCDBTlMVSeh9wvc;9G6pFLBodq z^lCYXz639ETi2uu_xfm|tcRd0b; zumz$bJs=YRdx$a}hZ~10thRzAm@N<} zNUT;tbnHUg_E8H^LMW!%A~~3aY?L`PZ8-%*G$s=%I%-l;H`7B+|I*a_jPF?&M@`gl zu6##LDTx_g#PJ6N;h`IC0e_*pkw^y9H%!c6JeF4{rRH(8#S_Rt1FCbV^fXg~hF;k? z+5!mZiWqG@CMy%kqpi1=C?Hf}9S+c~4F&Z9m##9oZpr!cOBmUmZF;)-ceTo|_VC z80}R6VGs+Gc!yx4D21sqG_s($5H~4G2mpmWps{+Sm}X<>8O+9lIBC0Hr)dVEAP~MZ zy|&Pt<87cq3CeWC5Vbz=np{yvbb~DBo)9AcL1K(RLzk4+5)~y%2Rinbrs9A5*MaNo V;r8m9{`d3t>ff{AnG???005_~{v!YY literal 0 Hc$@Wti6hXZE%w*73;Z-V<*DYpp5E@;6D6IaiaW>7uS{b*YpjBHKbl+dR*| z0Sw!`WdI-nAdcg&s;UYu?=7IUUIG~X&z}-#$+B!_j9H%}uqle-hs*m4G>o;@OCs9& z3ZNML_}ICXdQ rX}lND!;gU5crIkm-l}JPAn+LW(6Ri0&vE#)00000NkvXXu0mjf-{q-z literal 0 Hc$@No2g~i4uO(5kJ7W)|j`2?bk zbvj9vDv*#=!4HrWNf}cHY;99Xsvr`*TLjmQ$+CL0DQ*P|b2qcMGk?SVe_$i?o^Rg{ zyEBXc!!QiPFw8B*QW*PGTM9jVk{NVI(#)Msr*CVu+JhSeGKTN_L8Vgh01U4iD_a1n z)oLY|%Sn%?q*N;1mmQna8RUO}3!n}_4Hux%Tg>Nkuib8cuq^APs@S&eH~oJ9YZOJl zb%ayJ-EOyGEWv8E@>{LedjL-Wob2>6+I;}HiMG*bG+s<5lb=EeDXkFVVoz_TfZ1$z z<~YuC0F^`f8x*i3Z`*cfJRbizssf@Y`qOMSUjevxl|F+4cG!;NJo9~jNnXHUF!%=G zp^RR3`breAqt@&7zK8?HCJGRpPUj1pokw#bPH2g1}?}7pJGEuT}MN94j@T zcma<_qtSP*1$dr!9){tvUa!A{+Ab2%UjP6&-EQ~uX0urbLExv*Uw;wFC!a=Zpa9_V zwY$qHh05-3sFsC<5Q;f@DNG%J8ZLl305wNhz~xK83`bdjNsJYsa#t-&b)A(So&nSW zsNn*r15m>SnED};Pj(e}22cl}h6|t$Kn)i_9e^4xfI0v*I{x$H9{BEY3ZH*zhAF&^ z;f7(jPR;-h9os3Bc!)TTh39!^$oRk`^BIuO=TDHF9A&ZmO*||BD3{Auoj|98p8;7L zuGi~dg+d|oI|TrX#bTMYr;aU}tHE=hp=N)EVHk#C7>2nG`~_<1fUDAqA143+002ov JPDHLkV1flrFp~fP literal 0 Hc$@ - + 1 - + 2 - + 3 - + 4 - + 5 - + 6 @@ -61,14 +61,19 @@ - + 8 - + 9 + + + + + diff --git a/ui/guiutils.cpp b/ui/guiutils.cpp --- a/ui/guiutils.cpp +++ b/ui/guiutils.cpp @@ -73,7 +73,12 @@ if( ( (Okular::TextAnnotation*)ann )->textType() == Okular::TextAnnotation::Linked ) ret = i18n( "Pop-up Note" ); else - ret = i18n( "Inline Note" ); + { + if( ( (Okular::TextAnnotation*)ann )->inplaceIntent() == Okular::TextAnnotation::TypeWriter ) + ret = i18n( "Typewriter" ); + else + ret = i18n( "Inline Note" ); + } break; case Okular::Annotation::ALine: if( ( (Okular::LineAnnotation*)ann )->linePoints().count() == 2 ) diff --git a/ui/pagepainter.cpp b/ui/pagepainter.cpp --- a/ui/pagepainter.cpp +++ b/ui/pagepainter.cpp @@ -656,7 +656,7 @@ Okular::Annotation * a = *aIt; // honor opacity settings on supported types - unsigned int opacity = (unsigned int)( 255.0 * a->style().opacity() ); + unsigned int opacity = (unsigned int)( a->style().color().alpha() * a->style().opacity() ); // skip the annotation drawing if all the annotation is fully // transparent, but not with text annotations if ( opacity <= 0 && a->subType() != Okular::Annotation::AText ) @@ -686,6 +686,7 @@ image.fill( acolor.rgba() ); QPainter painter( &image ); painter.setFont( text->textFont() ); + painter.setPen( text->textColor() ); Qt::AlignmentFlag halign = ( text->inplaceAlignment() == 1 ? Qt::AlignHCenter : ( text->inplaceAlignment() == 2 ? Qt::AlignRight : Qt::AlignLeft ) ); const double invXScale = (double)page->width() / scaledWidth; const double invYScale = (double)page->height() / scaledHeight; diff --git a/ui/pageviewannotator.cpp b/ui/pageviewannotator.cpp --- a/ui/pageviewannotator.cpp +++ b/ui/pageviewannotator.cpp @@ -145,6 +145,60 @@ } } + void addTextNote( Okular::Annotation * &ann, Okular::TextAnnotation::InplaceIntent inplaceIntent, const QString summary ) + { + //note dialog + const QString prompt = i18n( "Text of the new note:" ); + bool resok; + const QString note = QInputDialog::getMultiLineText(nullptr, i18n( "New Text Note" ), prompt, QString(), &resok); + if(resok) + { + //add note + Okular::TextAnnotation * ta = new Okular::TextAnnotation(); + ann = ta; + ta->setFlags( ta->flags() | Okular::Annotation::FixedRotation ); + ta->setContents( note ); + ta->setTextType( Okular::TextAnnotation::InPlace ); + ta->setInplaceIntent( inplaceIntent ); + //set alignment + if ( m_annotElement.hasAttribute( QStringLiteral("align") ) ) + ta->setInplaceAlignment( m_annotElement.attribute( QStringLiteral("align") ).toInt() ); + //set font + if ( m_annotElement.hasAttribute( QStringLiteral("font") ) ) + { + QFont f; + f.fromString( m_annotElement.attribute( QStringLiteral("font") ) ); + ta->setTextFont( f ); + } + //set width + if ( m_annotElement.hasAttribute( QStringLiteral ( "width" ) ) ) + { + ta->style().setWidth( m_annotElement.attribute( QStringLiteral ( "width" ) ).toDouble() ); + } + // set font color + if ( m_engineElement.hasAttribute( QStringLiteral("color") ) ) + { + if ( inplaceIntent == Okular::TextAnnotation::TypeWriter ) + ta->setTextColor( m_engineElement.attribute( QStringLiteral("color") ) ); + else + ta->setTextColor( Qt::black ); + } + //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 @@ -160,49 +214,9 @@ const QString typeString = m_annotElement.attribute( QStringLiteral("type") ); // create TextAnnotation from path if ( typeString == QLatin1String("FreeText")) //setFlags( ta->flags() | Okular::Annotation::FixedRotation ); - ta->setContents( note ); - ta->setTextType( Okular::TextAnnotation::InPlace ); - //set alignment - if ( m_annotElement.hasAttribute( QStringLiteral("align") ) ) - ta->setInplaceAlignment( m_annotElement.attribute( QStringLiteral("align") ).toInt() ); - //set font - if ( m_annotElement.hasAttribute( QStringLiteral("font") ) ) - { - QFont f; - f.fromString( m_annotElement.attribute( QStringLiteral("font") ) ); - ta->setTextFont( f ); - } - //set width - if ( m_annotElement.hasAttribute( QStringLiteral ( "width" ) ) ) - { - ta->style().setWidth( m_annotElement.attribute( QStringLiteral ( "width" ) ).toDouble() ); - } - //set boundary - rect.left = qMin(startpoint.x,point.x); - rect.top = qMin(startpoint.y,point.y); - rect.right = qMax(startpoint.x,point.x); - rect.bottom = qMax(startpoint.y,point.y); - qCDebug(OkularUiDebug).nospace() << "xyScale=" << xscale << "," << yscale; - static const int padding = 2; - const QFontMetricsF mf(ta->textFont()); - const QRectF rcf = mf.boundingRect( Okular::NormalizedRect( rect.left, rect.top, 1.0, 1.0 ).geometry( (int)pagewidth, (int)pageheight ).adjusted( padding, padding, -padding, -padding ), - Qt::AlignTop | Qt::AlignLeft | Qt::TextWordWrap, ta->contents() ); - rect.right = qMax(rect.right, rect.left+(rcf.width()+padding*2)/pagewidth); - rect.bottom = qMax(rect.bottom, rect.top+(rcf.height()+padding*2)/pageheight); - ta->window().setSummary( i18n( "Inline Note" ) ); - } - } + addTextNote( ann, Okular::TextAnnotation::Unknown, i18n("Inline Note") ); + else if ( typeString == QLatin1String("Typewriter")) + addTextNote( ann, Okular::TextAnnotation::TypeWriter, i18n("Typewriter") ); else if ( typeString == QLatin1String("Text")) { Okular::TextAnnotation * ta = new Okular::TextAnnotation(); @@ -271,7 +285,6 @@ m_creationCompleted = false; clicked = false; - // safety check if ( !ann ) return QList< Okular::Annotation* >(); @@ -1044,6 +1057,8 @@ 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 ); @@ -1105,6 +1120,8 @@ return i18n( "Strike out" ); else if ( annotType == QLatin1String("underline") ) return i18n( "Underline" ); + else if ( annotType == QLatin1String("typewriter") ) + return i18n( "Typewriter" ); else return QString(); } @@ -1247,6 +1264,12 @@ p.drawLine( 1, 13, 16, 13 ); p.drawLine( 0, 20, 19, 20 ); } + else if ( annotType == QLatin1String("typewriter") ) + { + QImage overlay( QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString("okular/pics/tool-typewriter-okular-colorizable" + imageVariant + ".png") ) ); + GuiUtils::colorizeImage( overlay, engineColor ); + p.drawImage( QPoint(-2,2), overlay ); + } else { /* Unrecognized annotation type -- It shouldn't happen */