mirror of
https://invent.kde.org/graphics/okular
synced 2024-11-05 18:34:53 +00:00
1143 lines
38 KiB
C++
1143 lines
38 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2007 by Pino Toscano <pino@kde.org> *
|
|
* 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 <intevation@intevation.de> *
|
|
* *
|
|
* 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 <qbuttongroup.h>
|
|
#include <QKeyEvent>
|
|
#include <QMenu>
|
|
#include <QEvent>
|
|
#include <klineedit.h>
|
|
#include <KLocalizedString>
|
|
#include <kstandardaction.h>
|
|
#include <qaction.h>
|
|
#include <QUrl>
|
|
|
|
// local includes
|
|
#include "core/form.h"
|
|
#include "core/document.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<QAbstractButton *>(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::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<Okular::FormFieldButton *>( 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<Okular::FormFieldButton *>( dynamic_cast<FormWidgetIface*>(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 activation
|
|
// 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;
|
|
}
|
|
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()
|
|
{
|
|
}
|
|
|
|
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<Okular::FormFieldButton *>(m_ff);
|
|
FormWidgetIface::setFormWidgetsController( controller );
|
|
m_controller->registerRadioButton( this, form );
|
|
setChecked( form->state() );
|
|
}
|
|
|
|
void CheckBoxEdit::doActivateAction()
|
|
{
|
|
Okular::FormFieldButton *form = static_cast<Okular::FormFieldButton *>(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<Okular::FormFieldButton *>(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<Okular::FormFieldButton *>(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<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( event->globalPos() );
|
|
delete menu;
|
|
}
|
|
|
|
void FormLineEdit::slotChanged()
|
|
{
|
|
Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(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<Okular::FormFieldText *> ( 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() );
|
|
}
|
|
|
|
TextAreaEdit::~TextAreaEdit()
|
|
{
|
|
// We need this because otherwise on destruction we destruct the syntax highlighter
|
|
// that ends up calling text changed but then we go to slotChanged and we're already
|
|
// half destructed and all is bad
|
|
disconnect( this, &QTextEdit::textChanged, this, &TextAreaEdit::slotChanged );
|
|
}
|
|
|
|
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<QAction *> 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()
|
|
{
|
|
Okular::FormFieldText *form = static_cast<Okular::FormFieldText *>(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<Okular::FormFieldText *> ( 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<Okular::FormFieldText *>(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<Okular::FormFieldChoice *>(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<Okular::FormFieldChoice *>(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<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( 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 );
|
|
}
|
|
|
|
// 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 )
|
|
|
|
#undef DEFINE_ADDITIONAL_ACTIONS
|
|
|
|
#include "moc_formwidgets.cpp"
|