diff --git a/core/signatureutils.cpp b/core/signatureutils.cpp index 7388b9f3d..53da00c6e 100644 --- a/core/signatureutils.cpp +++ b/core/signatureutils.cpp @@ -1,158 +1,152 @@ /*************************************************************************** * Copyright (C) 2018 by Chinmoy Ranjan Pradhan * * * * 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 "signatureutils.h" using namespace Okular; CertificateInfo::CertificateInfo() { } CertificateInfo::~CertificateInfo() { } Q_DECLARE_OPERATORS_FOR_FLAGS( CertificateInfo::KeyUsages ) QByteArray CertificateInfo::version() const { return QByteArray(); } QString CertificateInfo::issuerName() const { return QString(); } QString CertificateInfo::issuerDN() const { return QString(); } QByteArray CertificateInfo::serialNumber() const { return QByteArray(); } QDateTime CertificateInfo::validityStart() const { return QDateTime(); } QDateTime CertificateInfo::validityEnd() const { return QDateTime(); } CertificateInfo::KeyUsages CertificateInfo::keyUsages() const { return KuNone; } QByteArray CertificateInfo::publicKey() const { return QByteArray(); } CertificateInfo::PublicKeyType CertificateInfo::publicKeyType() const { return OtherKey; } int CertificateInfo::publicKeyStrength() const { return -1; } bool CertificateInfo::isSelfSigned() const { return false; } QByteArray CertificateInfo::certificateData() const { return QByteArray(); } SignatureInfo::SignatureInfo() { } SignatureInfo::~SignatureInfo() { } SignatureInfo::SignatureStatus SignatureInfo::signatureStatus() const { return SignatureStatusUnknown; } SignatureInfo::CertificateStatus SignatureInfo::certificateStatus() const { return CertificateStatusUnknown; } SignatureInfo::HashAlgorithm SignatureInfo::hashAlgorithm() const { return HashAlgorithmUnknown; } QString SignatureInfo::subjectName() const { return QString(); } QString SignatureInfo::subjectDN() const { return QString(); } QDateTime SignatureInfo::signingTime() const { return QDateTime(); } QByteArray SignatureInfo::signature() const { return QByteArray(); } QList SignatureInfo::signedRangeBounds() const { return QList(); } bool SignatureInfo::signsTotalDocument() const { return false; } QString SignatureInfo::location() const { return QString(); } QString SignatureInfo::reason() const { return QString(); } -QByteArray *SignatureInfo::signedVersion( const QString &origFile ) -{ - Q_UNUSED( origFile ); - return nullptr; -} - CertificateInfo *SignatureInfo::certificateInfo() const { return nullptr; } diff --git a/core/signatureutils.h b/core/signatureutils.h index 49f3e713b..4cbc6118b 100644 --- a/core/signatureutils.h +++ b/core/signatureutils.h @@ -1,266 +1,261 @@ /*************************************************************************** * Copyright (C) 2018 by Chinmoy Ranjan Pradhan * * * * 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 OKULAR_SIGNATUREINFO_H #define OKULAR_SIGNATUREINFO_H #include "okularcore_export.h" #include #include #include #include #include namespace Okular { class CertificateInfoPrivate; class SignatureInfoPrivate; /** * @short A helper class to store information x509 digital certificate */ class OKULARCORE_EXPORT CertificateInfo { public: /** * The algorithm of public key. */ enum PublicKeyType { RsaKey, DsaKey, EcKey, OtherKey }; /** * Certificate key usage. */ enum KeyUsage { KuNone = 0, KuDigitalSignature = 1, KuNonRepudiation = 2, KuKeyEncipherment = 4, KuDataEncipherment = 8, KuKeyAgreement = 16, KuKeyCertSign = 32, KuClrSign = 64, KuEncipherOnly = 128 }; Q_DECLARE_FLAGS( KeyUsages, KeyUsage ) /** * Destructor */ virtual ~CertificateInfo(); /** * The certificate version string in hex encoding. */ virtual QByteArray version() const; /** * The common name of certificate issuer. */ virtual QString issuerName() const; /** * The distinguished name of certificate issuer. */ virtual QString issuerDN() const; /** The hex encoded certificate serial number. */ virtual QByteArray serialNumber() const; /** The date-time when certificate becomes valid. */ virtual QDateTime validityStart() const; /** * The date-time when certificate expires. */ virtual QDateTime validityEnd() const; /** * The key usages of certificate. */ virtual KeyUsages keyUsages() const; /** * The public key value. */ virtual QByteArray publicKey() const; /** * The public key type. */ virtual PublicKeyType publicKeyType() const; /** * The strength of public key in bits or -1 in case * key type is 'OtherKey'. */ virtual int publicKeyStrength() const; /** * Returns true if certificate is self signed; otherwise returns false. */ virtual bool isSelfSigned() const; /** * The DER encoded certificate. */ virtual QByteArray certificateData() const; protected: CertificateInfo(); private: Q_DISABLE_COPY( CertificateInfo ) }; /** * @short A helper class to store information about digital signature */ class OKULARCORE_EXPORT SignatureInfo { public: /** * The verfication result of the signature. */ enum SignatureStatus { SignatureStatusUnknown, ///< The signature status is unknown for some reason. SignatureValid, ///< The signature is cryptographically valid. SignatureInvalid, ///< The signature is cryptographically invalid. SignatureDigestMismatch, ///< The document content was changed after the signature was applied. SignatureDecodingError, ///< The signature CMS/PKCS7 structure is malformed. SignatureGenericError, ///< The signature could not be verified. SignatureNotFound, ///< The requested signature is not present in the document. SignatureNotVerified ///< The signature is not yet verified. }; /** * The verification result of the certificate. */ enum CertificateStatus { CertificateStatusUnknown, ///< The certificate status is unknown for some reason. CertificateTrusted, ///< The certificate is considered trusted. CertificateUntrustedIssuer, ///< The issuer of this certificate has been marked as untrusted by the user. CertificateUnknownIssuer, ///< The certificate trust chain has not finished in a trusted root certificate. CertificateRevoked, ///< The certificate was revoked by the issuing certificate authority. CertificateExpired, ///< The signing time is outside the validity bounds of this certificate. CertificateGenericError, ///< The certificate could not be verified. CertificateNotVerified ///< The certificate is not yet verified. }; /** * The hash algorithm of the signature */ enum HashAlgorithm { HashAlgorithmUnknown, HashAlgorithmMd2, HashAlgorithmMd5, HashAlgorithmSha1, HashAlgorithmSha256, HashAlgorithmSha384, HashAlgorithmSha512, HashAlgorithmSha224 }; /** * Destructor. */ virtual ~SignatureInfo(); /** * The signature status of the signature. */ virtual SignatureStatus signatureStatus() const; /** * The certificate status of the signature. */ virtual CertificateStatus certificateStatus() const; /** * The signer subject common name associated with the signature. */ virtual QString subjectName() const; /** * The signer subject distinguished name associated with the signature. */ virtual QString subjectDN() const; /** * The the hash algorithm used for the signature. */ virtual HashAlgorithm hashAlgorithm() const; /** * The signing time associated with the signature. */ virtual QDateTime signingTime() const; /** * Get the signature binary data. */ virtual QByteArray signature() const; /** * Get the bounds of the ranges of the document which are signed. */ virtual QList signedRangeBounds() const; /** * Checks whether the signature authenticates the total document * except for the signature itself. */ virtual bool signsTotalDocument() const; /** * Get location. */ virtual QString location() const; /** * Get signing reason. */ virtual QString reason() const; - /** - * Get signed version. - */ - virtual QByteArray *signedVersion( const QString &origFile ); - /** * Get certificate details. */ virtual CertificateInfo *certificateInfo() const; protected: SignatureInfo(); private: Q_DISABLE_COPY( SignatureInfo ) }; } #endif diff --git a/generators/poppler/pdfsignatureutils.cpp b/generators/poppler/pdfsignatureutils.cpp index e548dbb8e..101020a8b 100644 --- a/generators/poppler/pdfsignatureutils.cpp +++ b/generators/poppler/pdfsignatureutils.cpp @@ -1,247 +1,233 @@ /*************************************************************************** * Copyright (C) 2018 by Chinmoy Ranjan Pradhan * * * * 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 - #include "pdfsignatureutils.h" PopplerCertificateInfo::PopplerCertificateInfo( const Poppler::CertificateInfo &info ) : m_info( new Poppler::CertificateInfo( nullptr ) ) { *m_info = info; } PopplerCertificateInfo::~PopplerCertificateInfo() { } QByteArray PopplerCertificateInfo::version() const { return m_info->version(); } QString PopplerCertificateInfo::issuerName() const { return m_info->issuerName(); } QString PopplerCertificateInfo::issuerDN() const { return m_info->issuerDN(); } QByteArray PopplerCertificateInfo::serialNumber() const { return m_info->serialNumber(); } QDateTime PopplerCertificateInfo::validityStart() const { return m_info->validityStart(); } QDateTime PopplerCertificateInfo::validityEnd() const { return m_info->validityEnd(); } PopplerCertificateInfo::KeyUsages PopplerCertificateInfo::keyUsages() const { Poppler::CertificateInfo::KeyUsages popplerKu = m_info->keyUsages(); KeyUsages ku = KuNone; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuDigitalSignature ) ) ku |= KuDigitalSignature; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuNonRepudiation ) ) ku |= KuNonRepudiation; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuKeyEncipherment ) ) ku |= KuKeyEncipherment; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuDataEncipherment ) ) ku |= KuDataEncipherment; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuKeyAgreement ) ) ku |= KuKeyAgreement; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuKeyCertSign ) ) ku |= KuKeyCertSign; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuClrSign ) ) ku |= KuClrSign; if ( popplerKu.testFlag( Poppler::CertificateInfo::KuEncipherOnly ) ) ku |= KuEncipherOnly; return ku; } QByteArray PopplerCertificateInfo::publicKey() const { return m_info->publicKey(); } PopplerCertificateInfo::PublicKeyType PopplerCertificateInfo::publicKeyType() const { switch ( m_info->publicKeyType() ) { case Poppler::CertificateInfo::RsaKey: return RsaKey; case Poppler::CertificateInfo::DsaKey: return DsaKey; case Poppler::CertificateInfo::EcKey: return EcKey; case Poppler::CertificateInfo::OtherKey: return OtherKey; } } int PopplerCertificateInfo::publicKeyStrength() const { return m_info->publicKeyStrength(); } bool PopplerCertificateInfo::isSelfSigned() const { return m_info->isSelfSigned(); } QByteArray PopplerCertificateInfo::certificateData() const { return m_info->certificateData(); } PopplerSignatureInfo::PopplerSignatureInfo( const Poppler::SignatureValidationInfo &info ) - : m_info( new Poppler::SignatureValidationInfo( nullptr ) ), m_revisionData( new QByteArray ) + : m_info( new Poppler::SignatureValidationInfo( nullptr ) ) { *m_info = info; } PopplerSignatureInfo::~PopplerSignatureInfo() { } PopplerSignatureInfo::SignatureStatus PopplerSignatureInfo::signatureStatus() const { switch ( m_info->signatureStatus() ) { case Poppler::SignatureValidationInfo::SignatureValid: return SignatureValid; case Poppler::SignatureValidationInfo::SignatureInvalid: return SignatureInvalid; case Poppler::SignatureValidationInfo::SignatureDigestMismatch: return SignatureDigestMismatch; case Poppler::SignatureValidationInfo::SignatureDecodingError: return SignatureDecodingError; case Poppler::SignatureValidationInfo::SignatureGenericError: return SignatureGenericError; case Poppler::SignatureValidationInfo::SignatureNotFound: return SignatureNotFound; case Poppler::SignatureValidationInfo::SignatureNotVerified: return SignatureNotVerified; default: return SignatureStatusUnknown; } } PopplerSignatureInfo::CertificateStatus PopplerSignatureInfo::certificateStatus() const { switch ( m_info->certificateStatus() ) { case Poppler::SignatureValidationInfo::CertificateTrusted: return CertificateTrusted; case Poppler::SignatureValidationInfo::CertificateUntrustedIssuer: return CertificateUntrustedIssuer; case Poppler::SignatureValidationInfo::CertificateUnknownIssuer: return CertificateUnknownIssuer; case Poppler::SignatureValidationInfo::CertificateRevoked: return CertificateRevoked; case Poppler::SignatureValidationInfo::CertificateExpired: return CertificateExpired; case Poppler::SignatureValidationInfo::CertificateGenericError: return CertificateGenericError; case Poppler::SignatureValidationInfo::CertificateNotVerified: return CertificateNotVerified; default: return CertificateStatusUnknown; } } PopplerSignatureInfo::HashAlgorithm PopplerSignatureInfo::hashAlgorithm() const { switch ( m_info->hashAlgorithm() ) { case Poppler::SignatureValidationInfo::HashAlgorithmMd2: return HashAlgorithmMd2; case Poppler::SignatureValidationInfo::HashAlgorithmMd5: return HashAlgorithmMd5; case Poppler::SignatureValidationInfo::HashAlgorithmSha1: return HashAlgorithmSha1; case Poppler::SignatureValidationInfo::HashAlgorithmSha256: return HashAlgorithmSha256; case Poppler::SignatureValidationInfo::HashAlgorithmSha384: return HashAlgorithmSha384; case Poppler::SignatureValidationInfo::HashAlgorithmSha512: return HashAlgorithmSha512; case Poppler::SignatureValidationInfo::HashAlgorithmSha224: return HashAlgorithmSha224; default: return HashAlgorithmUnknown; } } QString PopplerSignatureInfo::subjectName() const { return m_info->signerName(); } QString PopplerSignatureInfo::subjectDN() const { return m_info->signerSubjectDN(); } QDateTime PopplerSignatureInfo::signingTime() const { return QDateTime::fromTime_t( m_info->signingTime() ); } QByteArray PopplerSignatureInfo::signature() const { return m_info->signature(); } QList PopplerSignatureInfo::signedRangeBounds() const { return m_info->signedRangeBounds(); } bool PopplerSignatureInfo::signsTotalDocument() const { return m_info->signsTotalDocument(); } QString PopplerSignatureInfo::location() const { return m_info->location(); } QString PopplerSignatureInfo::reason() const { return m_info->reason(); } -QByteArray *PopplerSignatureInfo::signedVersion( const QString &origFile ) -{ - auto byteRange = signedRangeBounds(); - QFile f( origFile ); - if ( f.open( QIODevice::ReadOnly ) && f.seek( byteRange.first() ) ) - { - const qint64 maxSize = byteRange.last() - byteRange.first(); - m_revisionData->append( f.read( maxSize ) ); - } - return m_revisionData.data(); -} - Okular::CertificateInfo *PopplerSignatureInfo::certificateInfo() const { return ( new PopplerCertificateInfo( m_info->certificateInfo() ) ); } diff --git a/generators/poppler/pdfsignatureutils.h b/generators/poppler/pdfsignatureutils.h index 2e5c46daf..33959b391 100644 --- a/generators/poppler/pdfsignatureutils.h +++ b/generators/poppler/pdfsignatureutils.h @@ -1,65 +1,63 @@ /*************************************************************************** * Copyright (C) 2018 by Chinmoy Ranjan Pradhan * * * * 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 _OKULAR_GENERATOR_PDF_SIGNATUREINFO_H_ #define _OKULAR_GENERATOR_PDF_SIGNATUREINFO_H_ #include #include "core/signatureutils.h" class PopplerCertificateInfo : public Okular::CertificateInfo { public: PopplerCertificateInfo(const Poppler::CertificateInfo &info); ~PopplerCertificateInfo(); QByteArray version() const override; QString issuerName() const override; QString issuerDN() const override; QByteArray serialNumber() const override; QDateTime validityStart() const override; QDateTime validityEnd() const override; KeyUsages keyUsages() const override; QByteArray publicKey() const override; PublicKeyType publicKeyType() const override; int publicKeyStrength() const override; bool isSelfSigned() const override; QByteArray certificateData() const override; private: Poppler::CertificateInfo *m_info; }; class PopplerSignatureInfo : public Okular::SignatureInfo { public: PopplerSignatureInfo( const Poppler::SignatureValidationInfo &info ); ~PopplerSignatureInfo(); SignatureStatus signatureStatus() const override; CertificateStatus certificateStatus() const override; QString subjectName() const override; QString subjectDN() const override; HashAlgorithm hashAlgorithm() const override; QDateTime signingTime() const override; QByteArray signature() const override; QList signedRangeBounds() const override; bool signsTotalDocument() const override; QString location() const override; QString reason() const override; - QByteArray *signedVersion( const QString &origFile ) override; Okular::CertificateInfo *certificateInfo() const override; private: Poppler::SignatureValidationInfo *m_info; - QScopedPointer m_revisionData; }; #endif diff --git a/ui/formwidgets.cpp b/ui/formwidgets.cpp index eead21ce7..02acd2428 100644 --- a/ui/formwidgets.cpp +++ b/ui/formwidgets.cpp @@ -1,1269 +1,1246 @@ /*************************************************************************** * Copyright (C) 2007 by Pino Toscano * * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group * * company, info@kdab.com. Work sponsored by the * * LiMux project of the city of Munich * * Copyright (C) 2018 Intevation GmbH * * * * 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 "formwidgets.h" #include "pageviewutils.h" #include "signaturewidgets.h" #include #include #include #include #include #include #include #include #include #include // local includes #include "core/form.h" #include "core/document.h" #include "core/signatureutils.h" #include "debug_ui.h" FormWidgetsController::FormWidgetsController( Okular::Document *doc ) : QObject( doc ), m_doc( doc ) { // emit changed signal when a form has changed connect( this, &FormWidgetsController::formTextChangedByUndoRedo, this, &FormWidgetsController::changed ); connect( this, &FormWidgetsController::formListChangedByUndoRedo, this, &FormWidgetsController::changed ); connect( this, &FormWidgetsController::formComboChangedByUndoRedo, this, &FormWidgetsController::changed ); // connect form modification signals to and from document connect( this, &FormWidgetsController::formTextChangedByWidget, doc, &Okular::Document::editFormText ); connect( doc, &Okular::Document::formTextChangedByUndoRedo, this, &FormWidgetsController::formTextChangedByUndoRedo ); connect( this, &FormWidgetsController::formListChangedByWidget, doc, &Okular::Document::editFormList ); connect( doc, &Okular::Document::formListChangedByUndoRedo, this, &FormWidgetsController::formListChangedByUndoRedo ); connect( this, &FormWidgetsController::formComboChangedByWidget, doc, &Okular::Document::editFormCombo ); connect( doc, &Okular::Document::formComboChangedByUndoRedo, this, &FormWidgetsController::formComboChangedByUndoRedo ); connect( this, &FormWidgetsController::formButtonsChangedByWidget, doc, &Okular::Document::editFormButtons ); connect( doc, &Okular::Document::formButtonsChangedByUndoRedo, this, &FormWidgetsController::slotFormButtonsChangedByUndoRedo ); // Connect undo/redo signals connect( this, &FormWidgetsController::requestUndo, doc, &Okular::Document::undo ); connect( this, &FormWidgetsController::requestRedo, doc, &Okular::Document::redo ); connect( doc, &Okular::Document::canUndoChanged, this, &FormWidgetsController::canUndoChanged ); connect( doc, &Okular::Document::canRedoChanged, this, &FormWidgetsController::canRedoChanged ); // Connect the generic formWidget refresh signal connect( doc, &Okular::Document::refreshFormWidget, this, &FormWidgetsController::refreshFormWidget ); } FormWidgetsController::~FormWidgetsController() { } void FormWidgetsController::signalAction( Okular::Action *a ) { emit action( a ); } void FormWidgetsController::registerRadioButton( FormWidgetIface *fwButton, Okular::FormFieldButton *formButton ) { if ( !fwButton ) return; QAbstractButton *button = dynamic_cast(fwButton); if ( !button ) { qWarning() << "fwButton is not a QAbstractButton" << fwButton; return; } QList< RadioData >::iterator it = m_radios.begin(), itEnd = m_radios.end(); const int id = formButton->id(); m_buttons.insert( id, button ); for ( ; it != itEnd; ++it ) { const QList< int >::const_iterator idsIt = qFind( (*it).ids, id ); if ( idsIt != (*it).ids.constEnd() ) { qCDebug(OkularUiDebug) << "Adding id" << id << "To group including" << (*it).ids; (*it).group->addButton( button ); (*it).group->setId( button, id ); return; } } const QList< int > siblings = formButton->siblings(); RadioData newdata; newdata.ids = siblings; newdata.ids.append( id ); newdata.group = new QButtonGroup(); newdata.group->addButton( button ); newdata.group->setId( button, id ); // Groups of 1 (like checkboxes) can't be exclusive if (siblings.isEmpty()) newdata.group->setExclusive( false ); connect( newdata.group, SIGNAL( buttonClicked(QAbstractButton* ) ), this, SLOT( slotButtonClicked( QAbstractButton* ) ) ); m_radios.append( newdata ); } -void FormWidgetsController::registerSignatureField(Okular::FormFieldSignature *formSignature) -{ - m_signatures.append(formSignature->id()); -} - -int FormWidgetsController::totalSignatures() -{ - return m_signatures.count(); -} - void FormWidgetsController::dropRadioButtons() { QList< RadioData >::iterator it = m_radios.begin(), itEnd = m_radios.end(); for ( ; it != itEnd; ++it ) { delete (*it).group; } m_radios.clear(); m_buttons.clear(); } bool FormWidgetsController::canUndo() { return m_doc->canUndo(); } bool FormWidgetsController::canRedo() { return m_doc->canRedo(); } void FormWidgetsController::slotButtonClicked( QAbstractButton *button ) { int pageNumber = -1; CheckBoxEdit *check = qobject_cast< CheckBoxEdit * >( button ); if ( check ) { // Checkboxes need to be uncheckable so if clicking a checked one // disable the exclusive status temporarily and uncheck it Okular::FormFieldButton *formButton = static_cast( check->formField() ); if ( formButton->state() ) { const bool wasExclusive = button->group()->exclusive(); button->group()->setExclusive(false); check->setChecked(false); button->group()->setExclusive(wasExclusive); } pageNumber = check->pageItem()->pageNumber(); } else if ( RadioButtonEdit *radio = qobject_cast< RadioButtonEdit * >( button ) ) { pageNumber = radio->pageItem()->pageNumber(); } const QList< QAbstractButton* > buttons = button->group()->buttons(); QList< bool > checked; QList< bool > prevChecked; QList< Okular::FormFieldButton*> formButtons; foreach ( QAbstractButton* button, buttons ) { checked.append( button->isChecked() ); Okular::FormFieldButton *formButton = static_cast( dynamic_cast(button)->formField() ); formButtons.append( formButton ); prevChecked.append( formButton->state() ); } if (checked != prevChecked) emit formButtonsChangedByWidget( pageNumber, formButtons, checked ); if ( check ) { // The formButtonsChangedByWidget signal changes the value of the underlying // Okular::FormField of the checkbox. We need to execute the activiation // action after this. check->doActivateAction(); } } void FormWidgetsController::slotFormButtonsChangedByUndoRedo( int pageNumber, const QList< Okular::FormFieldButton* > & formButtons) { foreach ( Okular::FormFieldButton* formButton, formButtons ) { int id = formButton->id(); QAbstractButton* button = m_buttons[id]; CheckBoxEdit *check = qobject_cast< CheckBoxEdit * >( button ); if ( check ) { emit refreshFormWidget( check->formField() ); } // temporarily disable exclusiveness of the button group // since it breaks doing/redoing steps into which all the checkboxes // are unchecked const bool wasExclusive = button->group()->exclusive(); button->group()->setExclusive(false); bool checked = formButton->state(); button->setChecked( checked ); button->group()->setExclusive(wasExclusive); button->setFocus(); } emit changed( pageNumber ); } FormWidgetIface * FormWidgetFactory::createWidget( Okular::FormField * ff, QWidget * parent ) { FormWidgetIface * widget = nullptr; switch ( ff->type() ) { case Okular::FormField::FormButton: { Okular::FormFieldButton * ffb = static_cast< Okular::FormFieldButton * >( ff ); switch ( ffb->buttonType() ) { case Okular::FormFieldButton::Push: widget = new PushButtonEdit( ffb, parent ); break; case Okular::FormFieldButton::CheckBox: widget = new CheckBoxEdit( ffb, parent ); break; case Okular::FormFieldButton::Radio: widget = new RadioButtonEdit( ffb, parent ); break; default: ; } break; } case Okular::FormField::FormText: { Okular::FormFieldText * fft = static_cast< Okular::FormFieldText * >( ff ); switch ( fft->textType() ) { case Okular::FormFieldText::Multiline: widget = new TextAreaEdit( fft, parent ); break; case Okular::FormFieldText::Normal: widget = new FormLineEdit( fft, parent ); break; case Okular::FormFieldText::FileSelect: widget = new FileEdit( fft, parent ); break; } break; } case Okular::FormField::FormChoice: { Okular::FormFieldChoice * ffc = static_cast< Okular::FormFieldChoice * >( ff ); switch ( ffc->choiceType() ) { case Okular::FormFieldChoice::ListBox: widget = new ListEdit( ffc, parent ); break; case Okular::FormFieldChoice::ComboBox: widget = new ComboEdit( ffc, parent ); break; } break; } case Okular::FormField::FormSignature: { Okular::FormFieldSignature * ffs = static_cast< Okular::FormFieldSignature * >( ff ); if ( ffs->isVisible() && ffs->signatureType() != Okular::FormFieldSignature::UnknownType ) widget = new SignatureEdit( ffs, parent ); break; } default: ; } if ( ff->isReadOnly() ) widget->setVisibility( false ); return widget; } FormWidgetIface::FormWidgetIface( QWidget * w, Okular::FormField * ff ) : m_controller( nullptr ), m_ff( ff ), m_widget( w ), m_pageItem( nullptr ) { } FormWidgetIface::~FormWidgetIface() { m_ff = nullptr; } Okular::NormalizedRect FormWidgetIface::rect() const { return m_ff->rect(); } void FormWidgetIface::setWidthHeight( int w, int h ) { m_widget->resize( w, h ); } void FormWidgetIface::moveTo( int x, int y ) { m_widget->move( x, y ); } bool FormWidgetIface::setVisibility( bool visible ) { bool hadfocus = m_widget->hasFocus(); if ( hadfocus ) m_widget->clearFocus(); m_widget->setVisible( visible ); return hadfocus; } void FormWidgetIface::setCanBeFilled( bool fill ) { m_widget->setEnabled( fill ); } void FormWidgetIface::setPageItem( PageViewItem *pageItem ) { m_pageItem = pageItem; } void FormWidgetIface::setFormField( Okular::FormField *field ) { m_ff = field; } Okular::FormField* FormWidgetIface::formField() const { return m_ff; } PageViewItem* FormWidgetIface::pageItem() const { return m_pageItem; } void FormWidgetIface::setFormWidgetsController( FormWidgetsController *controller ) { m_controller = controller; QObject *obj = dynamic_cast< QObject * > ( this ); QObject::connect( m_controller, &FormWidgetsController::refreshFormWidget, obj, [this] ( Okular::FormField *form ) { slotRefresh ( form ); }); } void FormWidgetIface::slotRefresh( Okular::FormField * form ) { if ( m_ff != form ) { return; } setVisibility( form->isVisible() && !form->isReadOnly() ); m_widget->setEnabled( !form->isReadOnly() ); } PushButtonEdit::PushButtonEdit( Okular::FormFieldButton * button, QWidget * parent ) : QPushButton( parent ), FormWidgetIface( this, button ) { setText( button->caption() ); setVisible( button->isVisible() ); setCursor( Qt::ArrowCursor ); } CheckBoxEdit::CheckBoxEdit( Okular::FormFieldButton * button, QWidget * parent ) : QCheckBox( parent ), FormWidgetIface( this, button ) { setText( button->caption() ); setVisible( button->isVisible() ); setCursor( Qt::ArrowCursor ); } void CheckBoxEdit::setFormWidgetsController( FormWidgetsController *controller ) { Okular::FormFieldButton *form = static_cast(m_ff); FormWidgetIface::setFormWidgetsController( controller ); m_controller->registerRadioButton( this, form ); setChecked( form->state() ); } void CheckBoxEdit::doActivateAction() { Okular::FormFieldButton *form = static_cast(m_ff); if ( form->activationAction() ) m_controller->signalAction( form->activationAction() ); } void CheckBoxEdit::slotRefresh( Okular::FormField * form ) { if ( form != m_ff ) { return; } FormWidgetIface::slotRefresh( form ); Okular::FormFieldButton *button = static_cast(m_ff); bool oldState = isChecked(); bool newState = button->state(); if ( oldState != newState ) { setChecked( button->state() ); doActivateAction(); } } RadioButtonEdit::RadioButtonEdit( Okular::FormFieldButton * button, QWidget * parent ) : QRadioButton( parent ), FormWidgetIface( this, button ) { setText( button->caption() ); setVisible( button->isVisible() ); setCursor( Qt::ArrowCursor ); } void RadioButtonEdit::setFormWidgetsController( FormWidgetsController *controller ) { Okular::FormFieldButton *form = static_cast(m_ff); FormWidgetIface::setFormWidgetsController( controller ); m_controller->registerRadioButton( this, form ); setChecked( form->state() ); } FormLineEdit::FormLineEdit( Okular::FormFieldText * text, QWidget * parent ) : QLineEdit( parent ), FormWidgetIface( this, text ) { int maxlen = text->maximumLength(); if ( maxlen >= 0 ) setMaxLength( maxlen ); setAlignment( text->textAlignment() ); setText( text->text() ); if ( text->isPassword() ) setEchoMode( QLineEdit::Password ); m_prevCursorPos = cursorPosition(); m_prevAnchorPos = cursorPosition(); connect( this, &QLineEdit::textEdited, this, &FormLineEdit::slotChanged ); connect( this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged ); setVisible( text->isVisible() ); } void FormLineEdit::setFormWidgetsController(FormWidgetsController* controller) { FormWidgetIface::setFormWidgetsController(controller); connect( m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &FormLineEdit::slotHandleTextChangedByUndoRedo ); } bool FormLineEdit::event( QEvent* e ) { if ( e->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast< QKeyEvent* >( e ); if ( keyEvent == QKeySequence::Undo ) { emit m_controller->requestUndo(); return true; } else if ( keyEvent == QKeySequence::Redo ) { emit m_controller->requestRedo(); return true; } } return QLineEdit::event( e ); } void FormLineEdit::contextMenuEvent( QContextMenuEvent* event ) { QMenu *menu = createStandardContextMenu(); QList actionList = menu->actions(); enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct }; QAction *kundo = KStandardAction::create( KStandardAction::Undo, m_controller, SIGNAL( requestUndo() ), menu ); QAction *kredo = KStandardAction::create( KStandardAction::Redo, m_controller, SIGNAL( requestRedo() ), menu ); connect( m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled ); connect( m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled ); kundo->setEnabled( m_controller->canUndo() ); kredo->setEnabled( m_controller->canRedo() ); QAction *oldUndo, *oldRedo; oldUndo = actionList[UndoAct]; oldRedo = actionList[RedoAct]; menu->insertAction( oldUndo, kundo ); menu->insertAction( oldRedo, kredo ); menu->removeAction( oldUndo ); menu->removeAction( oldRedo ); menu->exec( event->globalPos() ); delete menu; } void FormLineEdit::slotChanged() { Okular::FormFieldText *form = static_cast(m_ff); QString contents = text(); int cursorPos = cursorPosition(); if ( contents != form->text() ) { m_controller->formTextChangedByWidget( pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos ); } m_prevCursorPos = cursorPos; m_prevAnchorPos = cursorPos; if ( hasSelectedText() ) { if ( cursorPos == selectionStart() ) { m_prevAnchorPos = selectionStart() + selectedText().size(); } else { m_prevAnchorPos = selectionStart(); } } } void FormLineEdit::slotHandleTextChangedByUndoRedo( int pageNumber, Okular::FormFieldText* textForm, const QString & contents, int cursorPos, int anchorPos ) { Q_UNUSED(pageNumber); if ( textForm != m_ff || contents == text() ) { return; } disconnect( this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged ); setText(contents); setCursorPosition(anchorPos); cursorForward( true, cursorPos - anchorPos ); connect( this, &QLineEdit::cursorPositionChanged, this, &FormLineEdit::slotChanged ); m_prevCursorPos = cursorPos; m_prevAnchorPos = anchorPos; setFocus(); } void FormLineEdit::slotRefresh( Okular::FormField *form ) { if (form != m_ff) { return; } FormWidgetIface::slotRefresh( form ); Okular::FormFieldText *text = static_cast ( form ); setText( text->text() ); } TextAreaEdit::TextAreaEdit( Okular::FormFieldText * text, QWidget * parent ) : KTextEdit( parent ), FormWidgetIface( this, text ) { setAcceptRichText( text->isRichText() ); setCheckSpellingEnabled( text->canBeSpellChecked() ); setAlignment( text->textAlignment() ); setPlainText( text->text() ); setUndoRedoEnabled( false ); connect( this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged ); connect( this, &QTextEdit::cursorPositionChanged, this, &TextAreaEdit::slotChanged ); connect( this, &KTextEdit::aboutToShowContextMenu, this, &TextAreaEdit::slotUpdateUndoAndRedoInContextMenu ); m_prevCursorPos = textCursor().position(); m_prevAnchorPos = textCursor().anchor(); setVisible( text->isVisible() ); } bool TextAreaEdit::event( QEvent* e ) { if ( e->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast< QKeyEvent* >(e); if ( keyEvent == QKeySequence::Undo ) { emit m_controller->requestUndo(); return true; } else if ( keyEvent == QKeySequence::Redo ) { emit m_controller->requestRedo(); return true; } } return KTextEdit::event( e ); } void TextAreaEdit::slotUpdateUndoAndRedoInContextMenu( QMenu* menu ) { if ( !menu ) return; QList actionList = menu->actions(); enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs }; QAction *kundo = KStandardAction::create( KStandardAction::Undo, m_controller, SIGNAL( requestUndo() ), menu ); QAction *kredo = KStandardAction::create( KStandardAction::Redo, m_controller, SIGNAL( requestRedo() ), menu ); connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled ); connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled ); kundo->setEnabled( m_controller->canUndo() ); kredo->setEnabled( m_controller->canRedo() ); QAction *oldUndo, *oldRedo; oldUndo = actionList[UndoAct]; oldRedo = actionList[RedoAct]; menu->insertAction( oldUndo, kundo ); menu->insertAction( oldRedo, kredo ); menu->removeAction( oldUndo ); menu->removeAction( oldRedo ); } void TextAreaEdit::setFormWidgetsController( FormWidgetsController* controller ) { FormWidgetIface::setFormWidgetsController( controller ); connect( m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &TextAreaEdit::slotHandleTextChangedByUndoRedo ); } void TextAreaEdit::slotHandleTextChangedByUndoRedo( int pageNumber, Okular::FormFieldText* textForm, const QString & contents, int cursorPos, int anchorPos ) { Q_UNUSED(pageNumber); if ( textForm != m_ff ) { return; } setPlainText( contents ); QTextCursor c = textCursor(); c.setPosition( anchorPos ); c.setPosition( cursorPos,QTextCursor::KeepAnchor ); m_prevCursorPos = cursorPos; m_prevAnchorPos = anchorPos; setTextCursor( c ); setFocus(); } void TextAreaEdit::slotChanged() { // happens on destruction if (!m_ff) return; Okular::FormFieldText *form = static_cast(m_ff); QString contents = toPlainText(); int cursorPos = textCursor().position(); if (contents != form->text()) { m_controller->formTextChangedByWidget( pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos ); } m_prevCursorPos = cursorPos; m_prevAnchorPos = textCursor().anchor(); } void TextAreaEdit::slotRefresh( Okular::FormField *form ) { if (form != m_ff) { return; } FormWidgetIface::slotRefresh( form ); Okular::FormFieldText *text = static_cast ( form ); setPlainText( text->text() ); } FileEdit::FileEdit( Okular::FormFieldText * text, QWidget * parent ) : KUrlRequester( parent ), FormWidgetIface( this, text ) { setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly ); setFilter( i18n( "*|All Files" ) ); setUrl( QUrl::fromUserInput( text->text() ) ); lineEdit()->setAlignment( text->textAlignment() ); m_prevCursorPos = lineEdit()->cursorPosition(); m_prevAnchorPos = lineEdit()->cursorPosition(); connect( this, &KUrlRequester::textChanged, this, &FileEdit::slotChanged ); connect( lineEdit(), &QLineEdit::cursorPositionChanged, this, &FileEdit::slotChanged ); setVisible( text->isVisible() ); } void FileEdit::setFormWidgetsController( FormWidgetsController* controller ) { FormWidgetIface::setFormWidgetsController( controller ); connect( m_controller, &FormWidgetsController::formTextChangedByUndoRedo, this, &FileEdit::slotHandleFileChangedByUndoRedo ); } bool FileEdit::eventFilter( QObject* obj, QEvent* event ) { if ( obj == lineEdit() ) { if ( event->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast< QKeyEvent* >( event ); if ( keyEvent == QKeySequence::Undo ) { emit m_controller->requestUndo(); return true; } else if ( keyEvent == QKeySequence::Redo ) { emit m_controller->requestRedo(); return true; } } else if( event->type() == QEvent::ContextMenu ) { QContextMenuEvent *contextMenuEvent = static_cast< QContextMenuEvent* >( event ); QMenu *menu = ( (QLineEdit*) lineEdit() )->createStandardContextMenu(); QList< QAction* > actionList = menu->actions(); enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct }; QAction *kundo = KStandardAction::create( KStandardAction::Undo, m_controller, SIGNAL( requestUndo() ), menu ); QAction *kredo = KStandardAction::create( KStandardAction::Redo, m_controller, SIGNAL( requestRedo() ), menu ); connect(m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled ); connect(m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled ); kundo->setEnabled( m_controller->canUndo() ); kredo->setEnabled( m_controller->canRedo() ); QAction *oldUndo, *oldRedo; oldUndo = actionList[UndoAct]; oldRedo = actionList[RedoAct]; menu->insertAction( oldUndo, kundo ); menu->insertAction( oldRedo, kredo ); menu->removeAction( oldUndo ); menu->removeAction( oldRedo ); menu->exec( contextMenuEvent->globalPos() ); delete menu; return true; } } return KUrlRequester::eventFilter( obj, event ); } void FileEdit::slotChanged() { // Make sure line edit's text matches url expansion if ( text() != url().toLocalFile() ) this->setText( url().toLocalFile() ); Okular::FormFieldText *form = static_cast(m_ff); QString contents = text(); int cursorPos = lineEdit()->cursorPosition(); if (contents != form->text()) { m_controller->formTextChangedByWidget( pageItem()->pageNumber(), form, contents, cursorPos, m_prevCursorPos, m_prevAnchorPos ); } m_prevCursorPos = cursorPos; m_prevAnchorPos = cursorPos; if ( lineEdit()->hasSelectedText() ) { if ( cursorPos == lineEdit()->selectionStart() ) { m_prevAnchorPos = lineEdit()->selectionStart() + lineEdit()->selectedText().size(); } else { m_prevAnchorPos = lineEdit()->selectionStart(); } } } void FileEdit::slotHandleFileChangedByUndoRedo( int pageNumber, Okular::FormFieldText* form, const QString & contents, int cursorPos, int anchorPos ) { Q_UNUSED(pageNumber); if ( form != m_ff || contents == text() ) { return; } disconnect( this, SIGNAL( cursorPositionChanged( int, int ) ), this, SLOT( slotChanged() ) ); setText( contents ); lineEdit()->setCursorPosition( anchorPos ); lineEdit()->cursorForward( true, cursorPos - anchorPos ); connect( this, SIGNAL(cursorPositionChanged( int, int ) ), this, SLOT( slotChanged() ) ); m_prevCursorPos = cursorPos; m_prevAnchorPos = anchorPos; setFocus(); } ListEdit::ListEdit( Okular::FormFieldChoice * choice, QWidget * parent ) : QListWidget( parent ), FormWidgetIface( this, choice ) { addItems( choice->choices() ); setSelectionMode( choice->multiSelect() ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection ); setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); QList< int > selectedItems = choice->currentChoices(); if ( choice->multiSelect() ) { foreach ( int index, selectedItems ) if ( index >= 0 && index < count() ) item( index )->setSelected( true ); } else { if ( selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count() ) { setCurrentRow( selectedItems.at(0) ); scrollToItem( item( selectedItems.at(0) ) ); } } connect( this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged ); setVisible( choice->isVisible() ); setCursor( Qt::ArrowCursor ); } void ListEdit::setFormWidgetsController( FormWidgetsController* controller ) { FormWidgetIface::setFormWidgetsController( controller ); connect( m_controller, &FormWidgetsController::formListChangedByUndoRedo, this, &ListEdit::slotHandleFormListChangedByUndoRedo ); } void ListEdit::slotSelectionChanged() { QList< QListWidgetItem * > selection = selectedItems(); QList< int > rows; foreach( const QListWidgetItem * item, selection ) rows.append( row( item ) ); Okular::FormFieldChoice *form = static_cast(m_ff); if ( rows != form->currentChoices() ) { m_controller->formListChangedByWidget( pageItem()->pageNumber(), form, rows ); } } void ListEdit::slotHandleFormListChangedByUndoRedo( int pageNumber, Okular::FormFieldChoice* listForm, const QList< int > & choices ) { Q_UNUSED(pageNumber); if ( m_ff != listForm ) { return; } disconnect( this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged ); for(int i=0; i < count(); i++) { item( i )->setSelected( choices.contains(i) ); } connect( this, &QListWidget::itemSelectionChanged, this, &ListEdit::slotSelectionChanged ); setFocus(); } ComboEdit::ComboEdit( Okular::FormFieldChoice * choice, QWidget * parent ) : QComboBox( parent ), FormWidgetIface( this, choice ) { addItems( choice->choices() ); setEditable( true ); setInsertPolicy( NoInsert ); lineEdit()->setReadOnly( !choice->isEditable() ); QList< int > selectedItems = choice->currentChoices(); if ( selectedItems.count() == 1 && selectedItems.at(0) >= 0 && selectedItems.at(0) < count() ) setCurrentIndex( selectedItems.at(0) ); if ( choice->isEditable() && !choice->editChoice().isEmpty() ) lineEdit()->setText( choice->editChoice() ); connect( this, SIGNAL(currentIndexChanged(int)), this, SLOT(slotValueChanged()) ); connect( this, &QComboBox::editTextChanged, this, &ComboEdit::slotValueChanged ); connect( lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged ); setVisible( choice->isVisible() ); setCursor( Qt::ArrowCursor ); m_prevCursorPos = lineEdit()->cursorPosition(); m_prevAnchorPos = lineEdit()->cursorPosition(); } void ComboEdit::setFormWidgetsController(FormWidgetsController* controller) { FormWidgetIface::setFormWidgetsController(controller); connect( m_controller, &FormWidgetsController::formComboChangedByUndoRedo, this, &ComboEdit::slotHandleFormComboChangedByUndoRedo); } void ComboEdit::slotValueChanged() { const QString text = lineEdit()->text(); Okular::FormFieldChoice *form = static_cast(m_ff); QString prevText; if ( form->currentChoices().isEmpty() ) { prevText = form->editChoice(); } else { prevText = form->choices()[form->currentChoices()[0]]; } int cursorPos = lineEdit()->cursorPosition(); if ( text != prevText ) { m_controller->formComboChangedByWidget( pageItem()->pageNumber(), form, currentText(), cursorPos, m_prevCursorPos, m_prevAnchorPos ); } prevText = text; m_prevCursorPos = cursorPos; m_prevAnchorPos = cursorPos; if ( lineEdit()->hasSelectedText() ) { if ( cursorPos == lineEdit()->selectionStart() ) { m_prevAnchorPos = lineEdit()->selectionStart() + lineEdit()->selectedText().size(); } else { m_prevAnchorPos = lineEdit()->selectionStart(); } } } void ComboEdit::slotHandleFormComboChangedByUndoRedo( int pageNumber, Okular::FormFieldChoice* form, const QString & text, int cursorPos, int anchorPos ) { Q_UNUSED(pageNumber); if ( m_ff != form ) { return; } // Determine if text corrisponds to an index choices int index = -1; for ( int i = 0; i < count(); i++ ) { if ( itemText(i) == text ) { index = i; } } m_prevCursorPos = cursorPos; m_prevAnchorPos = anchorPos; disconnect( lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged ); const bool isCustomValue = index == -1; if ( isCustomValue ) { setEditText( text ); } else { setCurrentIndex( index ); } lineEdit()->setCursorPosition( anchorPos ); lineEdit()->cursorForward( true, cursorPos - anchorPos ); connect( lineEdit(), &QLineEdit::cursorPositionChanged, this, &ComboEdit::slotValueChanged ); setFocus(); } void ComboEdit::contextMenuEvent( QContextMenuEvent* event ) { QMenu *menu = lineEdit()->createStandardContextMenu(); QList actionList = menu->actions(); enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, DeleteAct, SelectAllAct }; QAction *kundo = KStandardAction::create( KStandardAction::Undo, m_controller, SIGNAL( requestUndo() ), menu ); QAction *kredo = KStandardAction::create( KStandardAction::Redo, m_controller, SIGNAL( requestRedo() ), menu ); connect( m_controller, &FormWidgetsController::canUndoChanged, kundo, &QAction::setEnabled ); connect( m_controller, &FormWidgetsController::canRedoChanged, kredo, &QAction::setEnabled ); kundo->setEnabled( m_controller->canUndo() ); kredo->setEnabled( m_controller->canRedo() ); QAction *oldUndo, *oldRedo; oldUndo = actionList[UndoAct]; oldRedo = actionList[RedoAct]; menu->insertAction( oldUndo, kundo ); menu->insertAction( oldRedo, kredo ); menu->removeAction( oldUndo ); menu->removeAction( oldRedo ); menu->exec( event->globalPos() ); delete menu; } bool ComboEdit::event( QEvent* e ) { if ( e->type() == QEvent::KeyPress ) { QKeyEvent *keyEvent = static_cast< QKeyEvent* >(e); if ( keyEvent == QKeySequence::Undo ) { emit m_controller->requestUndo(); return true; } else if ( keyEvent == QKeySequence::Redo ) { emit m_controller->requestRedo(); return true; } } return QComboBox::event( e ); } SignatureEdit::SignatureEdit( Okular::FormFieldSignature * signature, QWidget * parent ) : QAbstractButton( parent ), FormWidgetIface( this, signature ), m_leftMouseButtonPressed( false ) { Okular::FormFieldSignature *sigField = static_cast< Okular::FormFieldSignature * >( formField() ); m_sigInfo = sigField->validate(); setCheckable( false ); setCursor( Qt::PointingHandCursor ); connect( this, &SignatureEdit::clicked, this, &SignatureEdit::slotShowSummary ); } bool SignatureEdit::event( QEvent * e ) { switch ( e->type() ) { case QEvent::MouseButtonPress: { QMouseEvent *ev = static_cast< QMouseEvent * >( e ); if ( ev->button() == Qt::LeftButton ) { m_leftMouseButtonPressed = true; update(); } mousePressEvent( ev ); break; } case QEvent::MouseButtonRelease: { QMouseEvent *ev = static_cast< QMouseEvent * >( e ); m_leftMouseButtonPressed = false; if ( ev->button() == Qt::LeftButton) { update(); } mouseReleaseEvent( ev ); break; } default: break; } return QAbstractButton::event( e ); } void SignatureEdit::contextMenuEvent( QContextMenuEvent * event ) { QMenu *menu = new QMenu( this ); QAction *sigVal = new QAction( i18n("Validate Signature"), this ); menu->addAction( sigVal ); connect( sigVal, &QAction::triggered, this, &SignatureEdit::slotShowSummary ); QAction *sigProp = new QAction( i18n("Show Signature Properties"), this ); menu->addAction( sigProp ); connect( sigProp, &QAction::triggered, this, &SignatureEdit::slotShowProperties ); menu->exec( event->globalPos() ); delete menu; } void SignatureEdit::paintEvent( QPaintEvent * ) { QPainter painter( this ); painter.setPen( Qt::black ); if ( m_leftMouseButtonPressed ) { QColor col = palette().color( QPalette::Active, QPalette::Highlight ); col.setAlpha(50); painter.setBrush( col ); } else { painter.setBrush( Qt::transparent ); } painter.drawRect( 0, 0, width()-2, height()-2 ); } Okular::SignatureInfo *SignatureEdit::validate() { Okular::FormFieldSignature *sigField = static_cast< Okular::FormFieldSignature * >( formField() ); m_sigInfo = sigField->validate(); m_signatureValidated = true; return m_sigInfo; } -QByteArray *SignatureEdit::signedVersion( const QString &origFile ) -{ - return m_sigInfo->signedVersion( origFile ); -} - -void SignatureEdit::setFormWidgetsController( FormWidgetsController *controller ) -{ - Okular::FormFieldSignature *form = static_cast(m_ff); - FormWidgetIface::setFormWidgetsController( controller ); - m_controller->registerSignatureField( form ); -} - - void SignatureEdit::slotShowSummary() { SignatureSummaryDialog sigSummaryDlg( m_sigInfo, this ); sigSummaryDlg.exec(); } void SignatureEdit::slotShowProperties() { SignaturePropertiesDialog sigPropDlg( m_sigInfo, this ); sigPropDlg.exec(); } // Code for additional action handling. // Challenge: Change preprocessor magic to C++ magic! // // The mouseRelease event is special because the PDF spec // says that the activation action takes precedence over this. // So the mouse release action is only signaled if no activation // action exists. // // For checkboxes the activation action is not triggered as // they are still triggered from the clicked signal and additionally // when the checked state changes. #define DEFINE_ADDITIONAL_ACTIONS(FormClass, BaseClass) \ void FormClass::mousePressEvent( QMouseEvent *event ) \ { \ Okular::Action *act = m_ff->additionalAction( Okular::Annotation::MousePressed ); \ if ( act ) \ { \ m_controller->signalAction( act ); \ } \ BaseClass::mousePressEvent( event ); \ } \ void FormClass::mouseReleaseEvent( QMouseEvent *event ) \ { \ if ( !QWidget::rect().contains( event->localPos().toPoint() ) ) \ { \ BaseClass::mouseReleaseEvent( event ); \ return; \ } \ Okular::Action *act = m_ff->activationAction(); \ if ( act && !qobject_cast< CheckBoxEdit* > ( this ) ) \ { \ m_controller->signalAction( act ); \ } \ else if ( ( act = m_ff->additionalAction( Okular::Annotation::MouseReleased ) ) ) \ { \ m_controller->signalAction( act ); \ } \ BaseClass::mouseReleaseEvent( event ); \ } \ void FormClass::focusInEvent( QFocusEvent *event ) \ { \ Okular::Action *act = m_ff->additionalAction( Okular::Annotation::FocusIn ); \ if ( act ) \ { \ m_controller->signalAction( act ); \ } \ BaseClass::focusInEvent( event ); \ } \ void FormClass::focusOutEvent( QFocusEvent *event ) \ { \ Okular::Action *act = m_ff->additionalAction( Okular::Annotation::FocusOut ); \ if ( act ) \ { \ m_controller->signalAction( act ); \ } \ BaseClass::focusOutEvent( event ); \ } \ void FormClass::leaveEvent( QEvent *event ) \ { \ Okular::Action *act = m_ff->additionalAction( Okular::Annotation::CursorLeaving ); \ if ( act ) \ { \ m_controller->signalAction( act ); \ } \ BaseClass::leaveEvent( event ); \ } \ void FormClass::enterEvent( QEvent *event ) \ { \ Okular::Action *act = m_ff->additionalAction( Okular::Annotation::CursorEntering ); \ if ( act ) \ { \ m_controller->signalAction( act ); \ } \ BaseClass::enterEvent( event ); \ } DEFINE_ADDITIONAL_ACTIONS( PushButtonEdit, QPushButton ) DEFINE_ADDITIONAL_ACTIONS( CheckBoxEdit, QCheckBox ) DEFINE_ADDITIONAL_ACTIONS( RadioButtonEdit, QRadioButton ) DEFINE_ADDITIONAL_ACTIONS( FormLineEdit, QLineEdit ) DEFINE_ADDITIONAL_ACTIONS( TextAreaEdit, KTextEdit ) DEFINE_ADDITIONAL_ACTIONS( FileEdit, KUrlRequester ) DEFINE_ADDITIONAL_ACTIONS( ListEdit, QListWidget ) DEFINE_ADDITIONAL_ACTIONS( ComboEdit, QComboBox ) DEFINE_ADDITIONAL_ACTIONS( SignatureEdit, QAbstractButton ) #undef DEFINE_ADDITIONAL_ACTIONS #include "moc_formwidgets.cpp" diff --git a/ui/formwidgets.h b/ui/formwidgets.h index 4ef4cad96..2b81a655b 100644 --- a/ui/formwidgets.h +++ b/ui/formwidgets.h @@ -1,383 +1,378 @@ /*************************************************************************** * Copyright (C) 2007 by Pino Toscano * * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group * * company, info@kdab.com. Work sponsored by the * * LiMux project of the city of Munich * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #ifndef _OKULAR_FORMWIDGETS_H_ #define _OKULAR_FORMWIDGETS_H_ #include "core/area.h" #include #include #include #include #include #include #include #include class ComboEdit; class QMenu; class QButtonGroup; class FormWidgetIface; class PageViewItem; class RadioButtonEdit; class QEvent; namespace Okular { class Action; class FormField; class FormFieldButton; class FormFieldChoice; class FormFieldText; class FormFieldSignature; class Document; class SignatureInfo; } struct RadioData { RadioData() {} QList< int > ids; QButtonGroup *group; }; class FormWidgetsController : public QObject { Q_OBJECT public: FormWidgetsController( Okular::Document *doc ); virtual ~FormWidgetsController(); void signalAction( Okular::Action *action ); void registerRadioButton( FormWidgetIface *fwButton, Okular::FormFieldButton *formButton ); - void registerSignatureField( Okular::FormFieldSignature *formSignature ); - int totalSignatures(); void dropRadioButtons(); bool canUndo(); bool canRedo(); Q_SIGNALS: void changed( int pageNumber ); void requestUndo(); void requestRedo(); void canUndoChanged( bool undoAvailable ); void canRedoChanged( bool redoAvailable); void formTextChangedByWidget( int pageNumber, Okular::FormFieldText *form, const QString & newContents, int newCursorPos, int prevCursorPos, int prevAnchorPos ); void formTextChangedByUndoRedo( int pageNumber, Okular::FormFieldText *form, const QString & contents, int cursorPos, int anchorPos ); void formListChangedByWidget( int pageNumber, Okular::FormFieldChoice *form, const QList< int > & newChoices ); void formListChangedByUndoRedo( int pageNumber, Okular::FormFieldChoice *form, const QList< int > & choices ); void formComboChangedByWidget( int pageNumber, Okular::FormFieldChoice *form, const QString & newText, int newCursorPos, int prevCursorPos, int prevAnchorPos ); void formComboChangedByUndoRedo( int pageNumber, Okular::FormFieldChoice *form, const QString & text, int cursorPos, int anchorPos ); void formButtonsChangedByWidget( int pageNumber, const QList< Okular::FormFieldButton* > & formButtons, const QList< bool > & newButtonStates ); void action( Okular::Action *action ); void refreshFormWidget( Okular::FormField * form ); private Q_SLOTS: void slotButtonClicked( QAbstractButton *button ); void slotFormButtonsChangedByUndoRedo( int pageNumber, const QList< Okular::FormFieldButton* > & formButtons ); private: friend class TextAreaEdit; friend class FormLineEdit; friend class FileEdit; friend class ListEdit; friend class ComboEdit; QList< RadioData > m_radios; QHash< int, QAbstractButton* > m_buttons; Okular::Document* m_doc; - QList< int > m_signatures; }; class FormWidgetFactory { public: static FormWidgetIface * createWidget( Okular::FormField * ff, QWidget * parent = nullptr ); }; class FormWidgetIface { public: FormWidgetIface( QWidget * w, Okular::FormField * ff ); virtual ~FormWidgetIface(); Okular::NormalizedRect rect() const; void setWidthHeight( int w, int h ); void moveTo( int x, int y ); bool setVisibility( bool visible ); void setCanBeFilled( bool fill ); void setPageItem( PageViewItem *pageItem ); PageViewItem* pageItem() const; void setFormField( Okular::FormField *field ); Okular::FormField* formField() const; virtual void setFormWidgetsController( FormWidgetsController *controller ); protected: virtual void slotRefresh( Okular::FormField *form ); FormWidgetsController * m_controller; Okular::FormField * m_ff; private: QWidget * m_widget; PageViewItem * m_pageItem; }; #define DECLARE_ADDITIONAL_ACTIONS \ protected: \ virtual void mousePressEvent( QMouseEvent *event ) override; \ virtual void mouseReleaseEvent( QMouseEvent *event ) override; \ virtual void focusInEvent( QFocusEvent *event ) override; \ virtual void focusOutEvent( QFocusEvent *event ) override; \ virtual void leaveEvent( QEvent *event ) override; \ virtual void enterEvent( QEvent *event ) override; class PushButtonEdit : public QPushButton, public FormWidgetIface { Q_OBJECT public: explicit PushButtonEdit( Okular::FormFieldButton * button, QWidget * parent = nullptr ); DECLARE_ADDITIONAL_ACTIONS }; class CheckBoxEdit : public QCheckBox, public FormWidgetIface { Q_OBJECT public: explicit CheckBoxEdit( Okular::FormFieldButton * button, QWidget * parent = nullptr ); // reimplemented from FormWidgetIface void setFormWidgetsController( FormWidgetsController *controller ) override; void doActivateAction(); protected: void slotRefresh( Okular::FormField *form ) override; DECLARE_ADDITIONAL_ACTIONS }; class RadioButtonEdit : public QRadioButton, public FormWidgetIface { Q_OBJECT public: explicit RadioButtonEdit( Okular::FormFieldButton * button, QWidget * parent = nullptr ); // reimplemented from FormWidgetIface void setFormWidgetsController( FormWidgetsController *controller ) override; DECLARE_ADDITIONAL_ACTIONS }; class FormLineEdit : public QLineEdit, public FormWidgetIface { Q_OBJECT public: explicit FormLineEdit( Okular::FormFieldText * text, QWidget * parent = nullptr ); void setFormWidgetsController( FormWidgetsController *controller ) override; bool event ( QEvent * e ) override; void contextMenuEvent( QContextMenuEvent* event ) override; public Q_SLOTS: void slotHandleTextChangedByUndoRedo( int pageNumber, Okular::FormFieldText* textForm, const QString & contents, int cursorPos, int anchorPos ); private Q_SLOTS: void slotChanged(); protected: void slotRefresh( Okular::FormField* form ) override; private: int m_prevCursorPos; int m_prevAnchorPos; DECLARE_ADDITIONAL_ACTIONS }; class TextAreaEdit : public KTextEdit, public FormWidgetIface { Q_OBJECT public: explicit TextAreaEdit( Okular::FormFieldText * text, QWidget * parent = nullptr ); void setFormWidgetsController( FormWidgetsController *controller ) override; bool event ( QEvent * e ) override; public Q_SLOTS: void slotHandleTextChangedByUndoRedo( int pageNumber, Okular::FormFieldText * textForm, const QString & contents, int cursorPos, int anchorPos ); void slotUpdateUndoAndRedoInContextMenu( QMenu* menu ); private Q_SLOTS: void slotChanged(); protected: void slotRefresh( Okular::FormField* form ) override; private: int m_prevCursorPos; int m_prevAnchorPos; DECLARE_ADDITIONAL_ACTIONS }; class FileEdit : public KUrlRequester, public FormWidgetIface { Q_OBJECT public: explicit FileEdit( Okular::FormFieldText * text, QWidget * parent = nullptr ); void setFormWidgetsController( FormWidgetsController *controller ) override; protected: bool eventFilter( QObject *obj, QEvent *event ) override; private Q_SLOTS: void slotChanged(); void slotHandleFileChangedByUndoRedo( int pageNumber, Okular::FormFieldText * form, const QString & contents, int cursorPos, int anchorPos ); private: int m_prevCursorPos; int m_prevAnchorPos; DECLARE_ADDITIONAL_ACTIONS }; class ListEdit : public QListWidget, public FormWidgetIface { Q_OBJECT public: explicit ListEdit( Okular::FormFieldChoice * choice, QWidget * parent = nullptr ); void setFormWidgetsController( FormWidgetsController *controller ) override; private Q_SLOTS: void slotSelectionChanged(); void slotHandleFormListChangedByUndoRedo( int pageNumber, Okular::FormFieldChoice * listForm, const QList< int > & choices ); DECLARE_ADDITIONAL_ACTIONS }; class ComboEdit : public QComboBox, public FormWidgetIface { Q_OBJECT public: explicit ComboEdit( Okular::FormFieldChoice * choice, QWidget * parent = nullptr ); void setFormWidgetsController( FormWidgetsController *controller ) override; bool event ( QEvent * e ) override; void contextMenuEvent( QContextMenuEvent* event ) override; private Q_SLOTS: void slotValueChanged(); void slotHandleFormComboChangedByUndoRedo( int pageNumber, Okular::FormFieldChoice * comboForm, const QString & text, int cursorPos, int anchorPos ); private: int m_prevCursorPos; int m_prevAnchorPos; DECLARE_ADDITIONAL_ACTIONS }; class SignatureEdit : public QAbstractButton, public FormWidgetIface { Q_OBJECT public: explicit SignatureEdit( Okular::FormFieldSignature * signature, QWidget * parent = nullptr ); Okular::SignatureInfo *validate(); - void setFormWidgetsController( FormWidgetsController *controller ) override; - QByteArray *signedVersion( const QString &origFile ); protected: bool event( QEvent * e ) override; void contextMenuEvent( QContextMenuEvent * event ) override; void paintEvent( QPaintEvent * event ) override; private Q_SLOTS: void slotShowSummary(); void slotShowProperties(); private: Okular::SignatureInfo *m_sigInfo; bool m_leftMouseButtonPressed; bool m_signatureValidated; DECLARE_ADDITIONAL_ACTIONS }; #undef DECLARE_ADDITIONAL_ACTIONS #endif