You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

837 lines
37 KiB

/**************************************************************************
* Copyright (C) 2019 by Simone Gaiarin <simgunz@gmail.com> *
* *
* 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 "annotationactionhandler.h"
// qt includes
#include <QBitmap>
#include <QColorDialog>
#include <QFileInfo>
#include <QFontDialog>
#include <QPainter>
#include <QPen>
// kde includes
#include <KActionCollection>
#include <KLocalizedString>
#include <KMessageBox>
#include <KParts/MainWindow>
#include <KSelectAction>
#include <KToolBar>
// local includes
#include "annotationwidgets.h"
#include "guiutils.h"
#include "pageview.h"
#include "pageviewannotator.h"
#include "toggleactionmenu.h"
class AnnotationActionHandlerPrivate
{
public:
enum class AnnotationColor { Color, InnerColor };
static const QList<QPair<QString, QColor>> defaultColors;
static const QList<double> widthStandardValues;
static const QList<double> opacityStandardValues;
explicit AnnotationActionHandlerPrivate( AnnotationActionHandler * qq ) :
q( qq ),
annotator( nullptr ),
textTools( nullptr ),
textQuickTools( nullptr ),
agTools( nullptr ),
agLastAction( nullptr ),
aQuickTools( nullptr),
aGeomShapes( nullptr ),
aStamp( nullptr),
aAddToQuickTools( nullptr ),
aContinuousMode( nullptr ),
aWidth( nullptr ),
aColor( nullptr ),
aInnerColor( nullptr ),
aOpacity( nullptr ),
aFont( nullptr ),
aAdvancedSettings( nullptr ),
aHideToolBar( nullptr ),
aShowToolBar( nullptr ),
aCustomStamp(nullptr),
aCustomWidth( nullptr ),
aCustomOpacity( nullptr ),
currentColor( QColor() ),
currentInnerColor( QColor() ),
currentFont( QFont() ),
currentWidth( -1 ),
selectedTool( -1 ),
textToolsEnabled( false )
{}
QAction * selectActionItem( KSelectAction * aList, QAction * aCustomCurrent, double value,
const QList<double> &defaultValues, const QIcon &icon, const QString &label );
void selectStampActionItem( const QString &stampIconName );
void parseTool( int toolID );
void updateConfigActions( const QString &annotType = QLatin1String( "" ) );
void populateQuickAnnotations();
KSelectAction * colorPickerAction( AnnotationColor colorType );
const QIcon colorIcon( const QColor &color );
const QIcon widthIcon( double width );
const QIcon colorPickerIcon( const QString &iconName, const QColor &color );
const QIcon opacityIcon( double opacity );
const QIcon stampIcon( const QString &stampIconName );
void selectTool( int toolID );
void slotStampToolSelected( const QString &stamp );
void slotQuickToolSelected( int favToolID );
void slotSetColor( AnnotationColor colorType, const QColor &color = QColor() );
void slotSelectAnnotationFont();
void slotToolBarVisibilityChanged( bool checked );
AnnotationActionHandler * q;
PageViewAnnotator * annotator;
QList<QAction *> * textTools;
QList<QAction *> * textQuickTools;
QActionGroup * agTools;
QAction * agLastAction;
KSelectAction * aQuickTools;
ToggleActionMenu * aGeomShapes;
ToggleActionMenu * aStamp;
QAction * aAddToQuickTools;
KToggleAction * aContinuousMode;
KSelectAction * aWidth;
KSelectAction * aColor;
KSelectAction * aInnerColor;
KSelectAction * aOpacity;
QAction * aFont;
QAction * aAdvancedSettings;
QAction * aHideToolBar;
KToggleAction * aShowToolBar;
QAction * aCustomStamp;
QAction * aCustomWidth;
QAction * aCustomOpacity;
QColor currentColor;
QColor currentInnerColor;
QFont currentFont;
int currentWidth;
int selectedTool;
bool textToolsEnabled;
};
const QList<QPair<QString, QColor>> AnnotationActionHandlerPrivate::defaultColors = {
{ i18n( "Red" ), Qt::red },
{ i18n( "Orange" ), QColor( 255, 85, 0 ) },
{ i18n( "Yellow" ), Qt::yellow },
{ i18n( "Green" ), Qt::green },
{ i18n( "Cyan" ), Qt::cyan },
{ i18n( "Blue" ), Qt::blue },
{ i18n( "Magenta" ), Qt::magenta },
{ i18n( "White" ), Qt::white },
{ i18n( "Gray" ), Qt::gray },
{ i18n( "Black" ), Qt::black }
};
const QList<double> AnnotationActionHandlerPrivate::widthStandardValues = {
1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5
};
const QList<double> AnnotationActionHandlerPrivate::opacityStandardValues = {
10, 20, 30, 40, 50, 60, 70, 80, 90, 100
};
QAction * AnnotationActionHandlerPrivate::selectActionItem( KSelectAction * aList, QAction * aCustomCurrent,
double value, const QList<double> &defaultValues,
const QIcon &icon, const QString &label )
{
if ( aCustomCurrent ) {
aList->removeAction( aCustomCurrent );
delete aCustomCurrent;
}
QAction * aCustom = nullptr;
const int defaultValueIdx = defaultValues.indexOf( value );
if ( defaultValueIdx >= 0 ) {
aList->setCurrentItem( defaultValueIdx );
} else {
aCustom = new KToggleAction( icon, label, q );
const int aBeforeIdx = std::lower_bound(defaultValues.begin(), defaultValues.end(), value) - defaultValues.begin();
QAction * aBefore = aBeforeIdx < defaultValues.size() ? aList->actions().at( aBeforeIdx ) : nullptr;
aList->insertAction( aBefore, aCustom );
aList->setCurrentAction( aCustom );
}
return aCustom;
}
void AnnotationActionHandlerPrivate::selectStampActionItem( const QString &stampIconName )
{
auto it = std::find_if( StampAnnotationWidget::defaultStamps.begin(),
StampAnnotationWidget::defaultStamps.end(),
[&stampIconName] (const QPair<QString, QString>& element) {
return element.second == stampIconName;
}
);
bool defaultStamp = it != StampAnnotationWidget::defaultStamps.end();
if ( aCustomStamp ) {
aStamp->removeAction( aCustomStamp );
agTools->removeAction( aCustomStamp );
delete aCustomStamp;
aCustomStamp = nullptr;
}
if ( !defaultStamp ) {
QFileInfo info( stampIconName );
QString stampActionName = info.fileName();
aCustomStamp = new KToggleAction( stampIcon( stampIconName ), stampActionName, q);
aStamp->addAction( aCustomStamp );
aStamp->setDefaultAction( aCustomStamp );
agTools->addAction( aCustomStamp );
aCustomStamp->setChecked( true );
QObject::connect( aCustomStamp, &QAction::triggered, q, [this, stampIconName] () {
slotStampToolSelected( stampIconName );
} );
}
}
void AnnotationActionHandlerPrivate::parseTool( int toolID )
{
if ( toolID == -1 ) {
updateConfigActions();
return;
}
QDomElement toolElement = annotator->builtinTool( toolID );
const QString annotType = toolElement.attribute( QStringLiteral("type") );
QDomElement engineElement = toolElement.firstChildElement( QStringLiteral("engine") );
QDomElement annElement = engineElement.firstChildElement( QStringLiteral("annotation") );
QColor color, innerColor, textColor;
if ( annElement.hasAttribute( QStringLiteral( "color" ) ) ) {
color = QColor( annElement.attribute( QStringLiteral( "color" ) ) );
}
if ( annElement.hasAttribute( QStringLiteral( "innerColor" ) ) ) {
innerColor = QColor( annElement.attribute( QStringLiteral( "innerColor" ) ) );
}
if ( annElement.hasAttribute( QStringLiteral( "textColor" ) ) ) {
textColor = QColor( annElement.attribute( QStringLiteral( "textColor" ) ) );
}
if ( textColor.isValid() ) {
currentColor = textColor;
currentInnerColor = color;
} else {
currentColor = color;
currentInnerColor = innerColor;
}
if ( annElement.hasAttribute( QStringLiteral( "font" ) ) ) {
currentFont.fromString( annElement.attribute( QStringLiteral( "font" ) ) );
}
// if the width value is not a default one, insert a new action in the width list
if ( annElement.hasAttribute( QStringLiteral( "width" ) ) ) {
double width = annElement.attribute( QStringLiteral( "width" ) ).toDouble();
aCustomWidth = selectActionItem( aWidth, aCustomWidth, width, widthStandardValues,
widthIcon( width ), i18nc("@item:inlistbox", "Width %1", width ) );
}
// if the opacity value is not a default one, insert a new action in the opacity list
if ( annElement.hasAttribute( QStringLiteral( "opacity" ) ) ) {
double opacity = 100 * annElement.attribute( QStringLiteral( "opacity" ) ).toDouble();
aCustomOpacity = selectActionItem( aOpacity, aCustomOpacity, opacity, opacityStandardValues,
opacityIcon( opacity ), i18nc("@item:inlistbox", "%1\%", opacity) );
} else {
aOpacity->setCurrentItem( opacityStandardValues.size() - 1 ); // 100 %
}
// if the tool is a custom stamp, insert a new action in the stamp list
if ( annotType == QStringLiteral( "stamp" ) ) {
QString stampIconName = annElement.attribute( QStringLiteral("icon") );
selectStampActionItem( stampIconName );
}
updateConfigActions( annotType );
}
void AnnotationActionHandlerPrivate::updateConfigActions( const QString &annotType )
{
const bool isAnnotationSelected = !annotType.isEmpty();
const bool isTypewriter = annotType == QStringLiteral( "typewriter" );
const bool isInlineNote = annotType == QStringLiteral( "note-inline" );
const bool isText = isInlineNote || isTypewriter;
const bool isShape = annotType == QStringLiteral( "rectangle" )
|| annotType == QStringLiteral( "ellipse" )
|| annotType == QStringLiteral( "polygon" );
const bool isLine = annotType == QStringLiteral( "ink" )
|| annotType == QStringLiteral( "straight-line" );
const bool isStamp = annotType == QStringLiteral( "stamp" );
if ( isTypewriter ) {
aColor->setIcon( colorPickerIcon( QStringLiteral( "format-text-color" ), currentColor ) );
} else {
aColor->setIcon( colorPickerIcon( QStringLiteral( "format-stroke-color" ), currentColor ) );
}
aInnerColor->setIcon( colorPickerIcon( QStringLiteral( "format-fill-color" ), currentInnerColor ) );
aAddToQuickTools->setEnabled( isAnnotationSelected );
aWidth->setEnabled( isLine || isShape );
aColor->setEnabled( isAnnotationSelected && !isStamp );
aInnerColor->setEnabled( isShape );
aOpacity->setEnabled( isAnnotationSelected );
aFont->setEnabled( isText );
aAdvancedSettings->setEnabled( isAnnotationSelected );
// set tooltips
if ( !isAnnotationSelected ) {
aWidth->setToolTip( i18nc( "@info:tooltip", "Annotation line width (No annotation selected)") );
aColor->setToolTip( i18nc( "@info:tooltip", "Annotation color (No annotation selected)") );
aInnerColor->setToolTip( i18nc( "@info:tooltip", "Annotation fill color (No annotation selected)") );
aOpacity->setToolTip( i18nc( "@info:tooltip", "Annotation opacity (No annotation selected)") );
aFont->setToolTip( i18nc( "@info:tooltip", "Annotation font (No annotation selected)") );
aAddToQuickTools->setToolTip( i18nc( "@info:tooltip", "Add the current annotation to the quick annotations menu (No annotation selected)") );
aAdvancedSettings->setToolTip( i18nc( "@info:tooltip", "Advanced settings for the current annotation tool (No annotation selected)") );
return;
}
if ( isLine || isShape ) {
aWidth->setToolTip( i18nc( "@info:tooltip", "Annotation line width") );
} else {
aWidth->setToolTip( i18nc( "@info:tooltip", "Annotation line width (Current annotation has no line width)") );
}
if ( isTypewriter ) {
aColor->setToolTip( i18nc( "@info:tooltip", "Annotation text color") );
} else if ( isShape ) {
aColor->setToolTip( i18nc( "@info:tooltip", "Annotation border color") );
} else {
aColor->setToolTip( i18nc( "@info:tooltip", "Annotation color") );
}
if ( isShape ) {
aInnerColor->setToolTip( i18nc( "@info:tooltip", "Annotation fill color") );
} else {
aInnerColor->setToolTip( i18nc( "@info:tooltip", "Annotation fill color (Current annotation has no fill color)") );
}
if ( isText ) {
aFont->setToolTip( i18nc( "@info:tooltip", "Annotation font") );
} else {
aFont->setToolTip( i18nc( "@info:tooltip", "Annotation font (Current annotation has no font)") );
}
aOpacity->setToolTip( i18nc( "@info:tooltip", "Annotation opacity") );
aAddToQuickTools->setToolTip( i18nc( "@info:tooltip", "Add the current annotation to the quick annotations menu") );
aAdvancedSettings->setToolTip( i18nc( "@info:tooltip", "Advanced settings for the current annotation tool") );
}
void AnnotationActionHandlerPrivate::populateQuickAnnotations()
{
const QList<int> numberKeys = { Qt::Key_1, Qt::Key_2, Qt::Key_3, Qt::Key_4, Qt::Key_5,
Qt::Key_6, Qt::Key_7, Qt::Key_8, Qt::Key_9, Qt::Key_0
};
textQuickTools->clear();
aQuickTools->removeAllActions();
int favToolId = 1;
QList<int>::const_iterator shortcutNumber = numberKeys.begin();
QDomElement favToolElement = annotator->quickTool( favToolId );
while ( !favToolElement.isNull() ) {
QString itemText = favToolElement.attribute( QStringLiteral("name") );
if ( itemText.isEmpty() ) {
itemText = PageViewAnnotator::defaultToolName( favToolElement );
}
QIcon toolIcon = QIcon( PageViewAnnotator::makeToolPixmap( favToolElement ) );
QAction * annFav = new QAction( toolIcon, itemText, q);
aQuickTools->addAction( annFav );
if ( shortcutNumber != numberKeys.end() )
annFav->setShortcut( QKeySequence(Qt::ALT + * (shortcutNumber++) ) );
QObject::connect( annFav, &QAction::triggered, q, [this, favToolId] () {
slotQuickToolSelected( favToolId );
} );
QDomElement engineElement = favToolElement.firstChildElement( QStringLiteral("engine") );
if ( engineElement.attribute( QStringLiteral("type") ) == QStringLiteral("TextSelector") ) {
textQuickTools->append( annFav );
annFav->setEnabled( textToolsEnabled );
}
favToolElement = annotator->quickTool( ++favToolId );
}
QAction * separator = new QAction();
separator->setSeparator( true );
aQuickTools->addAction( separator );
// add action to open "Configure Annotation" settings dialog
KActionCollection * ac = qobject_cast<PageView *>( q->parent()->parent() )->actionCollection();
QAction * aConfigAnnotation = ac->action( QStringLiteral("options_configure_annotations") );
if ( aConfigAnnotation ) {
aQuickTools->addAction( aConfigAnnotation );
}
}
KSelectAction * AnnotationActionHandlerPrivate::colorPickerAction( AnnotationColor colorType )
{
auto colorList = defaultColors;
QString aText( i18nc( "@action:intoolbar Current annotation config option", "Color" ) );
if ( colorType == AnnotationColor::InnerColor ) {
aText = i18nc( "@action:intoolbar Current annotation config option", "Fill Color" );
colorList.append( QPair<QString, Qt::GlobalColor>( QStringLiteral( "Transparent" ), Qt::transparent ) );
}
KSelectAction * aColorPicker = new KSelectAction( QIcon(), aText, q);
aColorPicker->setToolBarMode( KSelectAction::MenuMode );
for ( const auto& colorNameValue : colorList ) {
QColor color( colorNameValue.second );
QAction * aColor = new QAction( colorIcon( color ), i18nc("@item:inlistbox Color name", "%1", colorNameValue.first ), q);
aColorPicker->addAction( aColor );
QObject::connect( aColor, &QAction::triggered, q, [this, colorType, color] () {
slotSetColor( colorType, color );
} );
}
QAction * aCustomColor = new QAction( QIcon::fromTheme( QStringLiteral("color-picker") ),
i18nc("@item:inlistbox", "Custom Color..."), q );
aColorPicker->addAction( aCustomColor );
QObject::connect( aCustomColor, &QAction::triggered, q, [this, colorType] () {
slotSetColor( colorType );
} );
return aColorPicker;
}
const QIcon AnnotationActionHandlerPrivate::widthIcon( double width )
{
QPixmap pm( 32, 32 );
pm.fill( Qt::transparent );
QPainter p( &pm );
p.setRenderHint( QPainter::Antialiasing );
p.setPen( QPen( Qt::black, 2 * width, Qt::SolidLine, Qt::RoundCap ) );
p.drawLine( 0, pm.height() / 2, pm.width(), pm.height() / 2 );
p.end();
return QIcon( pm );
}
const QIcon AnnotationActionHandlerPrivate::colorPickerIcon( const QString &iconName, const QColor &color )
{
QIcon icon = QIcon::fromTheme( iconName );
if ( !color.isValid() ) {
return icon;
}
QSize iconSize = QSize( 32, 32 );
QPixmap pm = icon.pixmap( iconSize );
QPainter p( &pm );
p.fillRect( 0, iconSize.height() - 8, iconSize.width(), 8, color );
p.end();
return QIcon( pm );
}
const QIcon AnnotationActionHandlerPrivate::colorIcon( const QColor &color )
{
QSize iconSize = QSize( 32, 32 );
QPixmap pm( iconSize );
QPainter p( &pm );
if ( color == Qt::transparent ) {
p.fillRect( 0, 0, iconSize.width(), iconSize.height(), Qt::white );
p.setPen( QPen( Qt::red, 2 ) );
p.drawLine( iconSize.width() - 1, 0, 0, iconSize.height() - 1 );
} else {
p.fillRect( 0, 0, iconSize.width(), iconSize.height(), color );
}
p.setPen( Qt::black );
p.drawRect( 0, 0, iconSize.width() - 1, iconSize.height() - 1 );
p.end();
return QIcon( pm );
}
const QIcon AnnotationActionHandlerPrivate::opacityIcon( double opacity )
{
QPixmap pm( 32, 32 );
pm.fill( Qt::transparent );
QPainter p( &pm );
p.setRenderHint( QPainter::Antialiasing );
p.setPen( Qt::NoPen );
QColor color( Qt::black );
color.setAlpha( opacity * 255 / 100 );
p.setBrush( QBrush( color ) );
p.drawRect( 4, 4, 24, 24 );
p.end();
return QIcon( pm );
}
const QIcon AnnotationActionHandlerPrivate::stampIcon( const QString &stampIconName )
{
QPixmap stampPix = GuiUtils::loadStamp( stampIconName, 32 );
if ( stampPix.width() == stampPix.height() )
return QIcon( stampPix );
else
return QIcon::fromTheme( QStringLiteral("tag") );
}
void AnnotationActionHandlerPrivate::selectTool( int toolID )
{
selectedTool = toolID;
annotator->selectTool( toolID );
parseTool( toolID );
}
void AnnotationActionHandlerPrivate::slotStampToolSelected( const QString &stamp )
{
KMessageBox::information( nullptr,
i18nc( "@info", "Stamps inserted in PDF documents are not visible in PDF readers other than Okular" ),
i18nc( "@title:window", "Experimental feature" ),
QStringLiteral( "stampAnnotationWarning" )
);
selectedTool = PageViewAnnotator::STAMP_TOOL_ID;
annotator->selectStampTool( stamp ); // triggers a reparsing thus calling parseTool
}
void AnnotationActionHandlerPrivate::slotQuickToolSelected( int favToolID )
{
int toolID = annotator->setQuickTool( favToolID ); // always triggers an unuseful reparsing
QAction * favToolAction = agTools->actions().at( toolID - 1 );
if ( !favToolAction->isChecked() ) {
// action group workaround: activates the action slot calling selectTool
// when new tool if different from the selected one
favToolAction->setChecked( true );
} else {
selectTool( toolID );
}
aShowToolBar->setChecked( true );
}
void AnnotationActionHandlerPrivate::slotSetColor( AnnotationColor colorType, const QColor &color )
{
QColor selectedColor( color );
if ( !selectedColor.isValid() ) {
selectedColor = QColorDialog::getColor( currentColor, nullptr,
i18nc( "@title:window", "Select color" ) );
if ( !selectedColor.isValid() ) {
return;
}
}
if ( colorType == AnnotationColor::Color ) {
currentColor = selectedColor;
annotator->setAnnotationColor( selectedColor );
} else if ( colorType == AnnotationColor::InnerColor ) {
currentInnerColor = selectedColor;
annotator->setAnnotationInnerColor( selectedColor );
}
}
void AnnotationActionHandlerPrivate::slotSelectAnnotationFont()
{
bool ok;
QFont selectedFont = QFontDialog::getFont( &ok, currentFont );
if ( ok ) {
currentFont = selectedFont;
annotator->setAnnotationFont( currentFont );
}
}
void AnnotationActionHandlerPrivate::slotToolBarVisibilityChanged( bool checked )
{
if ( !checked ) {
q->deselectAllAnnotationActions();
}
}
AnnotationActionHandler::AnnotationActionHandler( PageViewAnnotator * parent, KActionCollection * ac ) :
QObject( parent ),
d( new AnnotationActionHandlerPrivate( this ) )
{
d->annotator = parent;
// toolbar visibility actions
d->aShowToolBar = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-freehand") ),
i18n("&Annotations"),
this );
d->aHideToolBar = new QAction( QIcon::fromTheme( QStringLiteral("dialog-close") ),
i18nc("@action:intoolbar Hide the toolbar", "Hide"),
this );
connect( d->aHideToolBar, &QAction::triggered, this, [this] () {
d->aShowToolBar->setChecked( false );
} );
// Text markup actions
KToggleAction * aHighlighter = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-highlight") ),
i18nc("@action:intoolbar Annotation tool", "Highlighter"), this );
KToggleAction * aUnderline = new KToggleAction( QIcon::fromTheme( QStringLiteral("format-text-underline") ),
i18nc("@action:intoolbar Annotation tool", "Underline"), this) ;
KToggleAction * aSquiggle = new KToggleAction( QIcon::fromTheme( QStringLiteral("format-text-underline-squiggle") ),
i18nc("@action:intoolbar Annotation tool", "Squiggle"), this );
KToggleAction * aStrikeout = new KToggleAction( QIcon::fromTheme( QStringLiteral("format-text-strikethrough") ),
i18nc("@action:intoolbar Annotation tool", "Strike Out"), this );
// Notes actions
KToggleAction * aTypewriter = new KToggleAction( QIcon::fromTheme( QStringLiteral("tool-text") ),
i18nc("@action:intoolbar Annotation tool", "Typewriter"), this );
KToggleAction * aInlineNote = new KToggleAction( QIcon::fromTheme( QStringLiteral("note") ),
i18nc("@action:intoolbar Annotation tool", "Inline Note"), this );
KToggleAction * aPopupNote = new KToggleAction( QIcon::fromTheme( QStringLiteral("edit-comment") ),
i18nc("@action:intoolbar Annotation tool", "Popup Note"), this );
KToggleAction * aFreehandLine = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-freehand") ),
i18nc("@action:intoolbar Annotation tool", "Freehand Line"), this );
// Geometrical shapes actions
KToggleAction * aStraightLine = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-line") ),
i18nc("@action:intoolbar Annotation tool", "Straight line"), this );
KToggleAction * aArrow = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-arrow") ),
i18nc("@action:intoolbar Annotation tool", "Arrow"), this );
KToggleAction * aRectangle = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-rectangle") ),
i18nc("@action:intoolbar Annotation tool", "Rectangle"), this );
KToggleAction * aEllipse = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-ellipse") ),
i18nc("@action:intoolbar Annotation tool", "Ellipse"), this );
KToggleAction * aPolygon = new KToggleAction( QIcon::fromTheme( QStringLiteral("draw-polyline") ),
i18nc("@action:intoolbar Annotation tool", "Polygon"), this );
d->aGeomShapes = new ToggleActionMenu( QIcon(), QString(), this,
ToggleActionMenu::MenuButtonPopup,
ToggleActionMenu::ImplicitDefaultAction );
d->aGeomShapes->setText( i18nc( "@action", "Geometrical shapes" ) );
d->aGeomShapes->setEnabled( true ); // Need to explicitly set this once, or refreshActions() in part.cpp will disable this action
d->aGeomShapes->addAction( aArrow );
d->aGeomShapes->addAction( aStraightLine );
d->aGeomShapes->addAction( aRectangle );
d->aGeomShapes->addAction( aEllipse );
d->aGeomShapes->addAction( aPolygon );
d->aGeomShapes->setDefaultAction( aArrow );
// The order in which the actions are added is relevant to connect
// them to the correct toolId defined in tools.xml
d->agTools = new QActionGroup( this );
d->agTools->addAction( aHighlighter );
d->agTools->addAction( aUnderline );
d->agTools->addAction( aSquiggle );
d->agTools->addAction( aStrikeout );
d->agTools->addAction( aTypewriter );
d->agTools->addAction( aInlineNote );
d->agTools->addAction( aPopupNote );
d->agTools->addAction( aFreehandLine );
d->agTools->addAction( aArrow );
d->agTools->addAction( aStraightLine );
d->agTools->addAction( aRectangle );
d->agTools->addAction( aEllipse );
d->agTools->addAction( aPolygon );
d->textQuickTools = new QList<QAction *>();
d->textTools = new QList<QAction *>();
d->textTools->append( aHighlighter );
d->textTools->append( aUnderline );
d->textTools->append( aSquiggle );
d->textTools->append( aStrikeout );
int toolId = 1;
const QList<QAction *> tools = d->agTools->actions();
for ( const auto& ann : tools ) {
// action group workaround: connecting to toggled instead of triggered
connect( ann, &QAction::toggled, this, [this, toolId] ( bool checked ) {
if ( checked )
d->selectTool( toolId );
} );
toolId++;
}
// Stamp action
d->aStamp = new ToggleActionMenu( QIcon::fromTheme( QStringLiteral("tag") ),
QString(),
this,
ToggleActionMenu::MenuButtonPopup,
ToggleActionMenu::ImplicitDefaultAction );
d->aStamp->setText( i18nc( "@action", "Stamp" ) );
for ( const auto& stamp : StampAnnotationWidget::defaultStamps ) {
KToggleAction * ann = new KToggleAction( d->stampIcon( stamp.second ), stamp.first, this);
if ( !d->aStamp->defaultAction() )
d->aStamp->setDefaultAction( ann );
d->aStamp->addAction( ann );
d->agTools->addAction( ann );
// action group workaround: connecting to toggled instead of triggered
// (because deselectAllAnnotationActions has to call triggered)
connect( ann, &QAction::toggled, this, [this, stamp] ( bool checked ) {
if ( checked )
d->slotStampToolSelected( stamp.second );
} );
}
// Quick annotations action
d->aQuickTools = new KSelectAction( QIcon::fromTheme( QStringLiteral("draw-freehand") ),
i18nc("@action:intoolbar Show list of quick annotation tools", "Quick Annotations"), this );
d->aQuickTools->setToolBarMode( KSelectAction::MenuMode );
d->populateQuickAnnotations();
// Add to quick annotation action
d->aAddToQuickTools = new QAction(QIcon::fromTheme( QStringLiteral("favorite") ),
i18nc("@action:intoolbar Add current annotation tool to the quick annotations list", "Add to Quick Annotations"), this);
// Pin action
d->aContinuousMode = new KToggleAction(QIcon::fromTheme( QStringLiteral("pin") ),
i18nc("@action:intoolbar When checked keep the current annotation tool active after use", "Keep Active"), this);
d->aContinuousMode->setToolTip( i18nc( "@info:tooltip", "Keep the annotation tool active after use") );
d->aContinuousMode->setChecked( d->annotator->continuousMode() );
// Annotation settings actions
d->aColor = d->colorPickerAction( AnnotationActionHandlerPrivate::AnnotationColor::Color );
d->aInnerColor = d->colorPickerAction( AnnotationActionHandlerPrivate::AnnotationColor::InnerColor );
d->aFont = new QAction( QIcon::fromTheme( QStringLiteral("font-face") ),
i18nc("@action:intoolbar Current annotation config option", "Font"), this );
d->aAdvancedSettings = new QAction( QIcon::fromTheme( QStringLiteral("settings-configure") ),
i18nc("@action:intoolbar Current annotation advanced settings", "Annotation Settings"), this );
// Width list
d->aWidth = new KSelectAction( QIcon::fromTheme( QStringLiteral("edit-line-width") ),
i18nc("@action:intoolbar Current annotation config option", "Line width"), this );
d->aWidth->setToolBarMode( KSelectAction::MenuMode );
for ( auto width : d->widthStandardValues ) {
KToggleAction * ann = new KToggleAction( d->widthIcon( width ), i18nc("@item:inlistbox", "Width %1", width), this);
d->aWidth->addAction( ann );
connect( ann, &QAction::triggered, this, [this, width] () {
d->annotator->setAnnotationWidth( width );
} );
}
// Opacity list
d->aOpacity = new KSelectAction( QIcon::fromTheme( QStringLiteral("edit-opacity") ),
i18nc("@action:intoolbar Current annotation config option", "Opacity"), this );
d->aOpacity->setToolBarMode( KSelectAction::MenuMode );
for ( auto opacity : d->opacityStandardValues ) {
KToggleAction * ann = new KToggleAction( d->opacityIcon( opacity ), QStringLiteral( "%1\%" ).arg( opacity ), this);
d->aOpacity->addAction( ann );
connect( ann, &QAction::triggered, this, [this, opacity] () {
d->annotator->setAnnotationOpacity( opacity / 100 );
} );
}
connect( d->aAddToQuickTools, &QAction::triggered, d->annotator, &PageViewAnnotator::addToQuickAnnotations );
connect( d->aContinuousMode, &QAction::toggled, d->annotator, &PageViewAnnotator::setContinuousMode );
connect( d->aAdvancedSettings, &QAction::triggered, d->annotator, &PageViewAnnotator::slotAdvancedSettings );
connect( d->aFont, &QAction::triggered, std::bind( &AnnotationActionHandlerPrivate::slotSelectAnnotationFont, d ) );
// action group workaround: allows unchecking the currently selected annotation action.
// Other parts of code dependent to this workaround are marked with "action group workaround".
connect( d->agTools, &QActionGroup::triggered, this, [this]( QAction * action ) {
if (action == d->agLastAction) {
d->agLastAction = nullptr;
d->agTools->checkedAction()->setChecked( false );
d->selectTool( -1 );
} else {
d->agLastAction = action;
// Show the annotation toolbar whenever actions are triggered (e.g using shortcuts)
d->aShowToolBar->setChecked( true );
}
} );
ac->addAction( QStringLiteral("mouse_toggle_annotate"), d->aShowToolBar );
ac->addAction( QStringLiteral("hide_annotation_toolbar"), d->aHideToolBar );
ac->addAction( QStringLiteral("annotation_highlighter"), aHighlighter );
ac->addAction( QStringLiteral("annotation_underline"), aUnderline );
ac->addAction( QStringLiteral("annotation_squiggle"), aSquiggle );
ac->addAction( QStringLiteral("annotation_strike_out"), aStrikeout );
ac->addAction( QStringLiteral("annotation_typewriter"), aTypewriter );
ac->addAction( QStringLiteral("annotation_inline_note"), aInlineNote );
ac->addAction( QStringLiteral("annotation_popup_note"), aPopupNote );
ac->addAction( QStringLiteral("annotation_freehand_line"), aFreehandLine );
ac->addAction( QStringLiteral("annotation_arrow"), aArrow );
ac->addAction( QStringLiteral("annotation_straight_line"), aStraightLine );
ac->addAction( QStringLiteral("annotation_rectangle"), aRectangle );
ac->addAction( QStringLiteral("annotation_ellipse"), aEllipse );
ac->addAction( QStringLiteral("annotation_polygon"), aPolygon );
ac->addAction( QStringLiteral("annotation_geometrical_shape"), d->aGeomShapes );
ac->addAction( QStringLiteral("annotation_stamp"), d->aStamp );
ac->addAction( QStringLiteral("annotation_favorites"), d->aQuickTools );
ac->addAction( QStringLiteral("annotation_bookmark"), d->aAddToQuickTools );
ac->addAction(QStringLiteral("annotation_settings_pin"), d->aContinuousMode );
ac->addAction( QStringLiteral("annotation_settings_width"), d->aWidth );
ac->addAction(QStringLiteral("annotation_settings_color"), d->aColor );
ac->addAction(QStringLiteral("annotation_settings_inner_color"), d->aInnerColor );
ac->addAction( QStringLiteral("annotation_settings_opacity"), d->aOpacity );
ac->addAction(QStringLiteral("annotation_settings_font"), d->aFont );
ac->addAction(QStringLiteral("annotation_settings_advanced"), d->aAdvancedSettings );
ac->setDefaultShortcut( d->aShowToolBar, Qt::Key_F6 );
ac->setDefaultShortcut( aHighlighter, Qt::Key_1 );
ac->setDefaultShortcut( aUnderline, Qt::Key_2 );
ac->setDefaultShortcut( aSquiggle, Qt::Key_3 );
ac->setDefaultShortcut( aStrikeout, Qt::Key_4 );
ac->setDefaultShortcut( aTypewriter, Qt::Key_5 );
ac->setDefaultShortcut( aInlineNote, Qt::Key_6 );
ac->setDefaultShortcut( aPopupNote, Qt::Key_7 );
ac->setDefaultShortcut( aFreehandLine, Qt::Key_8 );
ac->setDefaultShortcut( aArrow, Qt::Key_9 );
ac->setDefaultShortcut( aRectangle, Qt::Key_0 );
ac->setDefaultShortcut( d->aAddToQuickTools, QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_B ) );
d->updateConfigActions();
}
AnnotationActionHandler::~AnnotationActionHandler()
{
// delete the private data storage structure
delete d;
}
void AnnotationActionHandler::setupAnnotationToolBarVisibilityAction()
{
// find the main window associated to the toggle toolbar action
QList<QWidget *> widgets = d->aShowToolBar->associatedWidgets();
auto itMainWindow = std::find_if( widgets.begin(), widgets.end(), [] (const QWidget * widget) {
return qobject_cast<const KParts::MainWindow *>( widget ) != nullptr;
} );
Q_ASSERT( itMainWindow != widgets.end() );
KParts::MainWindow * mw = qobject_cast<KParts::MainWindow *>(*itMainWindow);
// ensure that the annotation toolbar has been created and retrieve it
QList<KToolBar *> toolbars = mw->toolBars();
auto itToolBar = std::find_if( toolbars.begin(), toolbars.end(), [] (const KToolBar * toolBar) {
return toolBar->objectName() == QStringLiteral("annotationToolBar");
} );
Q_ASSERT( itToolBar != toolbars.end() );
KToolBar * annotationToolBar = mw->toolBar( QStringLiteral("annotationToolBar") );
d->aShowToolBar->setChecked( annotationToolBar->isVisible() );
connect( annotationToolBar, &QToolBar::visibilityChanged, d->aShowToolBar, &QAction::setChecked );
connect( d->aShowToolBar, &QAction::toggled, annotationToolBar, &KToolBar::setVisible );
connect( d->aShowToolBar, &QAction::toggled, this, [this] ( bool checked ) {
d->slotToolBarVisibilityChanged( checked );
});
}
void AnnotationActionHandler::reparseTools()
{
d->parseTool( d->selectedTool );
d->populateQuickAnnotations();
}
void AnnotationActionHandler::setToolsEnabled( bool on )
{
const QList<QAction *> tools = d->agTools->actions();
for ( QAction * ann : tools ) {
ann->setEnabled( on );
}
d->aQuickTools->setEnabled( on );
d->aGeomShapes->setEnabled( on );
d->aStamp->setEnabled( on );
d->aContinuousMode->setEnabled( on );
}
void AnnotationActionHandler::setTextToolsEnabled( bool on )
{
d->textToolsEnabled = on;
for ( QAction * ann : qAsConst( *d->textTools ) ) {
ann->setEnabled( on );
}
for ( QAction * ann : qAsConst( *d->textQuickTools ) ) {
ann->setEnabled( on );
}
}
void AnnotationActionHandler::deselectAllAnnotationActions()
{
QAction * checkedAction = d->agTools->checkedAction();
if ( checkedAction ) {
checkedAction->trigger(); // action group workaround: using trigger instead of setChecked
}
}
#include "moc_annotationactionhandler.cpp"