diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -356,6 +356,7 @@ part.cpp extensions.cpp ui/embeddedfilesdialog.cpp + ui/annotationactionhandler.cpp ui/annotwindow.cpp ui/annotationmodel.cpp ui/annotationpopup.cpp @@ -395,7 +396,7 @@ ui/thumbnaillist.cpp ui/toc.cpp ui/tocmodel.cpp - ui/toolaction.cpp + ui/toggleactionmenu.cpp ui/videowidget.cpp ui/layers.cpp ui/signatureguiutils.cpp @@ -458,7 +459,7 @@ ########### install files ############### -install(FILES okular.upd DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) +install(FILES okular.upd DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) install( FILES okular_part.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES part.rc part-viewermode.rc DESTINATION ${KDE_INSTALL_KXMLGUI5DIR}/okular ) diff --git a/conf/dlgannotations.cpp b/conf/dlgannotations.cpp --- a/conf/dlgannotations.cpp +++ b/conf/dlgannotations.cpp @@ -20,9 +20,9 @@ Ui_DlgAnnotationsBase dlg; dlg.setupUi( this ); - WidgetAnnotTools * kcfg_AnnotationTools = new WidgetAnnotTools( dlg.annotToolsGroup ); - dlg.annotToolsPlaceholder->addWidget( kcfg_AnnotationTools ); - kcfg_AnnotationTools->setObjectName( QStringLiteral("kcfg_AnnotationTools") ); + WidgetAnnotTools * kcfg_QuickAnnotationTools = new WidgetAnnotTools( dlg.annotToolsGroup ); + dlg.annotToolsPlaceholder->addWidget( kcfg_QuickAnnotationTools ); + kcfg_QuickAnnotationTools->setObjectName( QStringLiteral("kcfg_QuickAnnotationTools") ); KConfigDialogManager::changedMap()->insert( QStringLiteral("WidgetAnnotTools") , SIGNAL(changed()) ); } diff --git a/conf/dlgannotationsbase.ui b/conf/dlgannotationsbase.ui --- a/conf/dlgannotationsbase.ui +++ b/conf/dlgannotationsbase.ui @@ -84,7 +84,7 @@ - Annotation tools + Quick annotation tools diff --git a/conf/editannottooldialog.h b/conf/editannottooldialog.h --- a/conf/editannottooldialog.h +++ b/conf/editannottooldialog.h @@ -43,7 +43,9 @@ ToolTypewriter }; - explicit EditAnnotToolDialog( QWidget *parent = nullptr, const QDomElement &initialState = QDomElement() ); + explicit EditAnnotToolDialog( QWidget *parent = nullptr, + const QDomElement &initialState = QDomElement(), + bool builtinTool = false ); ~EditAnnotToolDialog(); QString name() const; QDomDocument toolXml() const; @@ -63,6 +65,8 @@ Okular::Annotation *m_stubann; AnnotationWidget *m_annotationWidget; + bool m_builtinTool; + private Q_SLOTS: void slotTypeChanged(); void slotDataChanged(); diff --git a/conf/editannottooldialog.cpp b/conf/editannottooldialog.cpp --- a/conf/editannottooldialog.cpp +++ b/conf/editannottooldialog.cpp @@ -34,9 +34,11 @@ #include "ui/pageviewannotator.h" -EditAnnotToolDialog::EditAnnotToolDialog( QWidget *parent, const QDomElement &initialState ) +EditAnnotToolDialog::EditAnnotToolDialog( QWidget *parent, const QDomElement &initialState, bool builtinTool ) : QDialog( parent ), m_stubann( nullptr ), m_annotationWidget( nullptr ) { + m_builtinTool = builtinTool; + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); @@ -54,21 +56,23 @@ mainLayout->addWidget(widget); mainLayout->addWidget(buttonBox); - m_name = new KLineEdit( widget ); + m_name->setReadOnly( m_builtinTool ); mainLayout->addWidget(m_name); tmplabel = new QLabel( i18n( "&Name:" ), widget ); mainLayout->addWidget(tmplabel); tmplabel->setBuddy( m_name ); widgetLayout->addWidget( tmplabel, 0, 0, Qt::AlignRight ); widgetLayout->addWidget( m_name, 0, 1 ); m_type = new KComboBox( false, widget ); + m_type->setVisible( !m_builtinTool ); mainLayout->addWidget(m_type); connect(m_type, static_cast(&KComboBox::currentIndexChanged), this, &EditAnnotToolDialog::slotTypeChanged); tmplabel = new QLabel( i18n( "&Type:" ), widget ); mainLayout->addWidget(tmplabel); tmplabel->setBuddy( m_type ); + tmplabel->setVisible( !m_builtinTool ); widgetLayout->addWidget( tmplabel, 1, 0, Qt::AlignRight ); widgetLayout->addWidget( m_type, 1, 1 ); @@ -381,6 +385,7 @@ } m_annotationWidget = AnnotationWidgetFactory::widgetFor( m_stubann ); + m_annotationWidget->setAnnotTypeEditable( !m_builtinTool ); m_appearanceBox->layout()->addWidget( m_annotationWidget->appearanceWidget() ); connect(m_annotationWidget, &AnnotationWidget::dataChanged, this, &EditAnnotToolDialog::slotDataChanged); diff --git a/conf/okular.kcfg b/conf/okular.kcfg --- a/conf/okular.kcfg +++ b/conf/okular.kcfg @@ -117,6 +117,47 @@ annotationTools + + + QStringList quickAnnotationTools; + // load the default tool list from the 'xml tools definition' file + QFile quickAnnFile( QStandardPaths::locate(QStandardPaths::GenericDataLocation, "okular/toolsQuick.xml") ); + if ( quickAnnFile.exists() && quickAnnFile.open( QIODevice::ReadOnly ) ) + { + QDomDocument doc; + if ( doc.setContent( &quickAnnFile ) ) + { + QDomElement toolsDefinition = doc.elementsByTagName("quickAnnotatingTools").item( 0 ).toElement(); + // create the quickAnnotationTools list from the XML dom tree + QDomNode toolDescription = toolsDefinition.firstChild(); + while ( toolDescription.isElement() ) + { + QDomElement toolElement = toolDescription.toElement(); + if ( toolElement.tagName() == "tool" ) + { + QDomDocument temp; + temp.appendChild( temp.importNode( toolElement, true) ); + // add each <tool>...</tool> as XML string + quickAnnotationTools << temp.toString(-1); + } + toolDescription = toolDescription.nextSibling(); + } + } + else + { + qWarning() << "QuickAnnotatingTools XML file seems to be damaged"; + } + } + else + { + qWarning() << "Unable to open QuickAnnotatingTools XML definition"; + } + + quickAnnotationTools + + + true + diff --git a/okular.upd b/okular.upd --- a/okular.upd +++ b/okular.upd @@ -23,3 +23,8 @@ Group=Dlg Presentation,Core Presentation Key=SlidesAdvance Key=SlidesAdvanceTime + +# remove user-defined annotation tools to make space to new builtin annotation tools +Id=annotation-toolbar +File=okularpartrc +Key=AnnotationTools,QuickAnnotationTools diff --git a/part.h b/part.h --- a/part.h +++ b/part.h @@ -53,7 +53,10 @@ class QTemporaryFile; class QAction; class QJsonObject; -namespace KParts { class GUIActivateEvent; } +namespace KParts { + class GUIActivateEvent; + class MainWindow; +} class FindBar; class ThumbnailList; @@ -262,7 +265,7 @@ void setupViewerActions(); void setViewerShortcuts(); - void setupActions(); + void setupActions( KParts::MainWindow * shell ); void setupPrint( QPrinter &printer ); bool doPrint( QPrinter &printer ); diff --git a/part.cpp b/part.cpp --- a/part.cpp +++ b/part.cpp @@ -62,13 +62,15 @@ #include #include #include +#include #include #include #include #include #include #include #include +#include #ifdef WITH_KWALLET #include #endif @@ -447,7 +449,7 @@ // [left toolbox: Reviews] | [] m_reviewsWidget = new Reviews( nullptr, m_document ); - m_sidebar->addItem( m_reviewsWidget, QIcon::fromTheme(QStringLiteral("draw-freehand")), i18n("Reviews") ); + m_sidebar->addItem( m_reviewsWidget, QIcon::fromTheme(QStringLiteral("draw-freehand")), i18n("Annotations") ); m_sidebar->setItemEnabled( m_reviewsWidget, false ); // [left toolbox: Bookmarks] | [] @@ -585,7 +587,8 @@ if ( m_embedMode != ViewerWidgetMode ) { - setupActions(); + KParts::MainWindow * shell = qobject_cast( parent ); + setupActions( shell ); } else { @@ -844,7 +847,7 @@ } } -void Part::setupActions() +void Part::setupActions( KParts::MainWindow * shell ) { KActionCollection * ac = actionCollection(); @@ -959,6 +962,14 @@ QAction *playPauseAction = new QAction( i18n( "Play/Pause Presentation" ), ac ); ac->addAction( QStringLiteral("presentation_play_pause"), playPauseAction ); playPauseAction->setEnabled( false ); + + // force the creation of the main toolbar before the annotation toolbar + // to respect the default toolbar layout defined in shell.rc + shell->toolBar( "mainToolBar" ); + KToggleToolBarAction * showAnnotationToolBar = new KToggleToolBarAction( shell->toolBar( "annotationToolBar" ), i18n("&Annotations"), this ); + showAnnotationToolBar->setIcon( QIcon::fromTheme( QStringLiteral("draw-freehand") ) ); + ac->addAction( QStringLiteral("mouse_toggle_annotate"), showAnnotationToolBar ); + ac->setDefaultShortcut( showAnnotationToolBar, Qt::Key_F6 ); } Part::~Part() diff --git a/part.rc b/part.rc --- a/part.rc +++ b/part.rc @@ -107,5 +107,30 @@ + + +Annotation Toolbar + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell/shell.rc b/shell/shell.rc --- a/shell/shell.rc +++ b/shell/shell.rc @@ -1,5 +1,5 @@ - + @@ -23,4 +23,7 @@ + diff --git a/ui/annotationactionhandler.h b/ui/annotationactionhandler.h new file mode 100644 --- /dev/null +++ b/ui/annotationactionhandler.h @@ -0,0 +1,51 @@ +/************************************************************************** +* Copyright (C) 2019 by Simone Gaiarin * +* * +* 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 _ANNOTATIONACTIONHANDLER_H_ +#define _ANNOTATIONACTIONHANDLER_H_ + +#include + +class QAction; +class QColor; +class QFont; +class KActionCollection; +class PageViewAnnotator; +class AnnotationActionHandlerPrivate; + +/** + * @short Handles all the actions of the annotation toolbar + */ +class AnnotationActionHandler : public QObject +{ + Q_OBJECT + + public: + AnnotationActionHandler( PageViewAnnotator * parent, KActionCollection * ac ); + ~AnnotationActionHandler(); + + /** + * @short Reads the settings for the current annotation and rebuild the quick annotations menu + * + * This method is called each time okularpartrc is modified. This happens in the following + * situations (among others): the quick annotations are modified from the KCM settings + * page, a tool is modified using the "advanced settings" action, a quick annotation is + * selected, an annotation property (line width, colors, opacity, font) is modified. + */ + void reparseTools(); + void setToolsEnabled( bool on ); + void setTextToolsEnabled( bool on ); + void deselectAllAnnotationActions(); + + private: + friend class AnnotationActionHandlerPrivate; + class AnnotationActionHandlerPrivate * d; +}; + +#endif // _ANNOTATIONACTIONHANDLER_H_ diff --git a/ui/annotationactionhandler.cpp b/ui/annotationactionhandler.cpp new file mode 100644 --- /dev/null +++ b/ui/annotationactionhandler.cpp @@ -0,0 +1,787 @@ +/************************************************************************** +* Copyright (C) 2019 by Simone Gaiarin * +* * +* 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 +#include +#include +#include +#include +#include + +// kde includes +#include +#include +#include +#include + +// local includes +#include "annotationwidgets.h" +#include "guiutils.h" +#include "pageview.h" +#include "pageviewannotator.h" +#include "toggleactionmenu.h" + + +static const QList widthStandardValues = { 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5 }; +static const QList opacityStandardValues = { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 }; + + +class AnnotationActionHandlerPrivate +{ + public: + enum class AnnotationColor { Color, InnerColor }; + static const QList> defaultColors; + + explicit AnnotationActionHandlerPrivate( AnnotationActionHandler * qq ) : + q( qq ), + annotator( nullptr ), + textTools( nullptr ), + textQuickTools( nullptr ), + agTools( nullptr ), + aQuickTools( nullptr), + aGeomShapes( nullptr ), + aStamp( nullptr), + aAddToQuickTools( nullptr ), + aContinuousMode( nullptr ), + aWidth( nullptr ), + aColor( nullptr ), + aInnerColor( nullptr ), + aOpacity( nullptr ), + aFont( nullptr ), + aAdvancedSettings( 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 &defaultValues, const QIcon &icon, const QString &label ); + void selectStampActionItem( const QString &stampIconName ); + void parseTool( int toolID ); + + void updateConfigActions( const QString &annotType = "" ); + void populateQuickAnnotations(); + KSelectAction * colorPickerAction( AnnotationColor colorType ); + + QIcon colorIcon( const QColor &color ); + QIcon widthIcon( double width ); + QIcon colorPickerIcon( const QString &iconName, const QColor &color ); + QIcon opacityIcon( double opacity ); + QIcon stampIcon( const QString &stampIconName ); + + void selectTool( int toolID ); + void slotStampToolSelected( QString stamp ); + void slotQuickToolSelected( int favToolID ); + void slotSetColor( const AnnotationColor &colorType, const QColor &color = QColor() ); + void slotSelectAnnotationFont(); + + AnnotationActionHandler * q; + + PageViewAnnotator * annotator; + + QList * textTools; + QList * textQuickTools; + QActionGroup * agTools; + + KSelectAction * aQuickTools; + ToggleActionMenu * aGeomShapes; + ToggleActionMenu * aStamp; + QAction * aAddToQuickTools; + KToggleAction * aContinuousMode; + KSelectAction * aWidth; + KSelectAction * aColor; + KSelectAction * aInnerColor; + KSelectAction * aOpacity; + QAction * aFont; + QAction * aAdvancedSettings; + QAction * aHideToolbar; + QAction * aShowToolBar; + + QAction * aCustomStamp; + QAction * aCustomWidth; + QAction * aCustomOpacity; + + QColor currentColor; + QColor currentInnerColor; + QFont currentFont; + int currentWidth; + + int selectedTool; + bool textToolsEnabled; +}; + +const QList> AnnotationActionHandlerPrivate::defaultColors = { + { i18n( "Red" ), Qt::red }, + { i18n( "Orange" ), "#ff5500" }, + { 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 } + +}; + +QAction * AnnotationActionHandlerPrivate::selectActionItem( KSelectAction * aList, QAction * aCustomCurrent, + double value, const QList &defaultValues, + const QIcon &icon, const QString &label ) +{ + QAction * aCustom = nullptr; + if ( aCustomCurrent ) { + aList->removeAction( aCustomCurrent ); + } + int idx = defaultValues.indexOf( value ); + if ( idx >= 0 ) { + aList->setCurrentItem( idx ); + } else { + aCustom = new KToggleAction( icon, label, q ); + int i = std::lower_bound(defaultValues.begin(), defaultValues.end(), value) - defaultValues.begin(); + QAction * before = i < defaultValues.size() ? aList->actions()[ i ] : nullptr; + aList->insertAction( before, aCustom ); + aList->setCurrentAction( aCustom ); + } + return aCustom; +} + +void AnnotationActionHandlerPrivate::selectStampActionItem( const QString &stampIconName ) +{ + QPair pair; + bool defaultStamp = false; + for ( const auto& pair : StampAnnotationWidget::defaultStamps ) { + if ( stampIconName == pair.second) { + defaultStamp = true; + break; + } + } + if ( aCustomStamp ) { + aStamp->removeAction( aCustomStamp ); + agTools->removeAction( 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, [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 annotationSelected = !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( annotationSelected ); + aWidth->setEnabled( isLine || isShape ); + aColor->setEnabled( annotationSelected && !isStamp ); + aInnerColor->setEnabled( isShape ); + aOpacity->setEnabled( annotationSelected && !isStamp ); + aFont->setEnabled( isText ); + aAdvancedSettings->setEnabled( annotationSelected ); + + // set tooltips + if ( !annotationSelected ) { + 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 ( isStamp ) { + aOpacity->setToolTip( i18nc( "@info:tooltip", "Annotation opacity (Cannot change opacity of current annotation)") ); + } else { + aOpacity->setToolTip( i18nc( "@info:tooltip", "Annotation opacity") ); + } + + if ( isText ) { + aFont->setToolTip( i18nc( "@info:tooltip", "Annotation font") ); + } else { + aFont->setToolTip( i18nc( "@info:tooltip", "Annotation font (Current annotation has no font)") ); + } + + 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 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::const_iterator shortcutNumber = numberKeys.begin(); + QDomElement favToolElement = annotator->quickTool( favToolId ); + while ( !favToolElement.isNull() ) { + QString itemText = favToolElement.attribute( "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, [this, favToolId] () { + slotQuickToolSelected( favToolId ); + } ); + + QDomElement engineElement = favToolElement.firstChildElement( QStringLiteral("engine") ); + if ( engineElement.attribute( "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( 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( 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, [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, [this, colorType] () { + slotSetColor( colorType ); + } ); + return aColorPicker; +} + +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 ); +} + +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 ); +} + +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 ); +} + +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 ); +} + +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( 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()[ 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( const 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 ); + } +} + +AnnotationActionHandler::AnnotationActionHandler( PageViewAnnotator * parent, KActionCollection * ac ) : + QObject( parent ), + d( new AnnotationActionHandlerPrivate( this ) ) +{ + d->annotator = parent; + + // 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, + QToolButton::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(); + d->textTools = new QList(); + d->textTools->append( aHighlighter ); + d->textTools->append( aUnderline ); + d->textTools->append( aSquiggle ); + d->textTools->append( aStrikeout ); + + int toolId = 1; + for ( QAction * ann : d->agTools->actions() ) { + // action group workaround: connecting to toggled instead of triggered + connect( ann, &QAction::toggled, [this, toolId] ( bool checked ) { + if ( checked ) + d->selectTool( toolId ); + } ); + toolId++; + } + + // Stamp action + d->aStamp = new ToggleActionMenu( QIcon::fromTheme( QStringLiteral("tag") ), + QString(), + this, + QToolButton::MenuButtonPopup, + ToggleActionMenu::ImplicitDefaultAction ); + d->aStamp->setText( i18nc( "@action", "Stamp" ) ); + + QPair 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, 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 ); + d->aHideToolbar = new QAction( QIcon::fromTheme( QStringLiteral("dialog-close") ), + i18nc("@action:intoolbar Hide the toolbar", "Hide"), 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 ( int i = 0; i < widthStandardValues.size(); i++ ) { + KToggleAction * ann = new KToggleAction( d->widthIcon( widthStandardValues[i] ), i18nc("@item:inlistbox", "Width %1", widthStandardValues[i]), this); + d->aWidth->addAction( ann ); + connect( ann, &QAction::triggered, [this, i] () { + d->annotator->setAnnotationWidth( widthStandardValues[i] ); + } ); + } + + // 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 ( int i = 0; i < opacityStandardValues.size(); i++ ) { + KToggleAction * ann = new KToggleAction( d->opacityIcon( opacityStandardValues[i] ), QString( "%1\%" ).arg( opacityStandardValues[i] ), this); + d->aOpacity->addAction( ann ); + connect( ann, &QAction::triggered, [this, i] () { + d->annotator->setAnnotationOpacity( opacityStandardValues[i] / 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]( QAction * action ) { + static QAction* lastAction = nullptr; + if (action == lastAction) { + lastAction = nullptr; + d->agTools->checkedAction()->setChecked( false ); + d->selectTool( -1 ); + } else { + lastAction = action; + } + } ); + + d->aShowToolBar = ac->action( "mouse_toggle_annotate" ); + connect( d->aHideToolbar, &QAction::triggered, [this] () { + d->aShowToolBar->setChecked( false ); + } ); + + 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->addAction( QStringLiteral("hide_annotation_toolbar"), d->aHideToolbar ); + + 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( d->aAddToQuickTools, QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_B ) ); + + d->updateConfigActions(); +} + +AnnotationActionHandler::~AnnotationActionHandler() +{ + // delete the private data storage structure + delete d; +} + +void AnnotationActionHandler::reparseTools() +{ + d->parseTool( d->selectedTool ); + d->populateQuickAnnotations(); +} + +void AnnotationActionHandler::setToolsEnabled( bool on ) +{ + for ( QAction * ann : d->agTools->actions() ) { + 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 : *d->textTools ) { + ann->setEnabled( on ); + } + for ( QAction * ann : *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" diff --git a/ui/annotationwidgets.h b/ui/annotationwidgets.h --- a/ui/annotationwidgets.h +++ b/ui/annotationwidgets.h @@ -89,6 +89,8 @@ virtual void applyChanges(); + void setAnnotTypeEditable( bool ); + Q_SIGNALS: void dataChanged(); @@ -102,6 +104,8 @@ void addOpacitySpinBox( QWidget * widget, QFormLayout * formlayout ); void addVerticalSpacer( QFormLayout * formlayout ); + bool m_typeEditable; + private: Okular::Annotation * m_ann; QWidget * m_appearanceWidget { nullptr }; @@ -153,6 +157,8 @@ Q_OBJECT public: + static const QList> defaultStamps; + explicit StampAnnotationWidget( Okular::Annotation * ann ); void applyChanges() override; diff --git a/ui/annotationwidgets.cpp b/ui/annotationwidgets.cpp --- a/ui/annotationwidgets.cpp +++ b/ui/annotationwidgets.cpp @@ -212,7 +212,7 @@ AnnotationWidget::AnnotationWidget( Okular::Annotation * ann ) - : m_ann( ann ) + : m_typeEditable( true ), m_ann( ann ) { } @@ -281,7 +281,7 @@ m_opacity = new QSpinBox( widget ); m_opacity->setRange( 0, 100 ); m_opacity->setValue( (int)( m_ann->style().opacity() * 100 ) ); - m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80 %'", " %" ) ); + m_opacity->setSuffix( i18nc( "Suffix for the opacity level, eg '80%'", "%" ) ); formlayout->addRow( i18n( "&Opacity:" ), m_opacity); connect( m_opacity, SIGNAL(valueChanged(int)), this, SIGNAL(dataChanged()) ); } @@ -346,6 +346,12 @@ } } + +void AnnotationWidget::setAnnotTypeEditable( bool editable ) +{ + m_typeEditable = editable; +} + void TextAnnotationWidget::createPopupNoteStyleUi( QWidget * widget, QFormLayout * formlayout ) { addColorButton( widget, formlayout ); addOpacitySpinBox( widget, formlayout ); @@ -421,6 +427,26 @@ connect( m_spinWidth, SIGNAL(valueChanged(double)), this, SIGNAL(dataChanged()) ); } +const QList> StampAnnotationWidget::defaultStamps = { + { i18n( "Approved" ), QStringLiteral("Approved") }, + { i18n( "As Is" ), QStringLiteral("AsIs") }, + { i18n( "Confidential" ), QStringLiteral("Confidential") }, + { i18n( "Departmental" ), QStringLiteral("Departmental") }, + { i18n( "Draft" ), QStringLiteral("Draft") }, + { i18n( "Experimental" ), QStringLiteral("Experimental") }, + { i18n( "Final" ), QStringLiteral("Expired") }, + { i18n( "For Comment" ), QStringLiteral("ForComment") }, + { i18n( "For Public Release" ), QStringLiteral("ForPublicRelease") }, + { i18n( "Not Approved" ), QStringLiteral("NotApproved") }, + { i18n( "Not For Public Release" ), QStringLiteral("NotForPublicRelease") }, + { i18n( "Sold" ), QStringLiteral("Sold") }, + { i18n( "Top Secret" ), QStringLiteral("TopSecret") }, + { i18n( "Bookmark" ), QStringLiteral("bookmark-new") }, + { i18n( "Information" ), QStringLiteral("help-about") }, + { i18n( "KDE" ), QStringLiteral("kde") }, + { i18n( "Okular" ), QStringLiteral("okular") } +}; + StampAnnotationWidget::StampAnnotationWidget( Okular::Annotation * ann ) : AnnotationWidget( ann ), m_pixmapSelector( nullptr ) { @@ -446,24 +472,12 @@ formlayout->addRow( i18n( "Stamp symbol:" ), m_pixmapSelector ); m_pixmapSelector->setEditable( true ); - m_pixmapSelector->addItem( i18n( "Okular" ), QStringLiteral("okular") ); - m_pixmapSelector->addItem( i18n( "Bookmark" ), QStringLiteral("bookmarks") ); - m_pixmapSelector->addItem( i18n( "KDE" ), QStringLiteral("kde") ); - m_pixmapSelector->addItem( i18n( "Information" ), QStringLiteral("help-about") ); - m_pixmapSelector->addItem( i18n( "Approved" ), QStringLiteral("Approved") ); - m_pixmapSelector->addItem( i18n( "As Is" ), QStringLiteral("AsIs") ); - m_pixmapSelector->addItem( i18n( "Confidential" ), QStringLiteral("Confidential") ); - m_pixmapSelector->addItem( i18n( "Departmental" ), QStringLiteral("Departmental") ); - m_pixmapSelector->addItem( i18n( "Draft" ), QStringLiteral("Draft") ); - m_pixmapSelector->addItem( i18n( "Experimental" ), QStringLiteral("Experimental") ); - m_pixmapSelector->addItem( i18n( "Expired" ), QStringLiteral("Expired") ); - m_pixmapSelector->addItem( i18n( "Final" ), QStringLiteral("Final") ); - m_pixmapSelector->addItem( i18n( "For Comment" ), QStringLiteral("ForComment") ); - m_pixmapSelector->addItem( i18n( "For Public Release" ), QStringLiteral("ForPublicRelease") ); - m_pixmapSelector->addItem( i18n( "Not Approved" ), QStringLiteral("NotApproved") ); - m_pixmapSelector->addItem( i18n( "Not For Public Release" ), QStringLiteral("NotForPublicRelease") ); - m_pixmapSelector->addItem( i18n( "Sold" ), QStringLiteral("Sold") ); - m_pixmapSelector->addItem( i18n( "Top Secret" ), QStringLiteral("TopSecret") ); + QPair pair; + foreach(pair, defaultStamps) + { + m_pixmapSelector->addItem(pair.first, pair.second); + } + m_pixmapSelector->setIcon( m_stampAnn->stampIconName() ); m_pixmapSelector->setPreviewSize( 64 ); @@ -673,8 +687,13 @@ { QWidget * widget = qobject_cast( formlayout->parent() ); + m_typeCombo = new KComboBox( widget ); - formlayout->addRow( i18n( "Type:" ), m_typeCombo ); + m_typeCombo->setVisible( m_typeEditable ); + if( m_typeEditable ) + { + formlayout->addRow( i18n( "Type:" ), m_typeCombo ); + } m_typeCombo->addItem( i18n( "Highlight" ) ); m_typeCombo->addItem( i18n( "Squiggle" ) ); m_typeCombo->addItem( i18n( "Underline" ) ); @@ -707,7 +726,11 @@ QWidget * widget = qobject_cast( formlayout->parent() ); m_typeCombo = new KComboBox( widget ); - formlayout->addRow( i18n( "Type:" ), m_typeCombo ); + m_typeCombo->setVisible( m_typeEditable ); + if( m_typeEditable ) + { + formlayout->addRow( i18n( "Type:" ), m_typeCombo ); + } addVerticalSpacer( formlayout ); addColorButton( widget, formlayout ); addOpacitySpinBox( widget, formlayout ); diff --git a/ui/data/CMakeLists.txt b/ui/data/CMakeLists.txt --- a/ui/data/CMakeLists.txt +++ b/ui/data/CMakeLists.txt @@ -4,6 +4,7 @@ # install annotator xml tools description install(FILES tools.xml + toolsQuick.xml drawingtools.xml DESTINATION ${KDE_INSTALL_DATADIR}/okular) diff --git a/ui/data/tools.xml b/ui/data/tools.xml --- a/ui/data/tools.xml +++ b/ui/data/tools.xml @@ -1,79 +1,128 @@ - - - + + + - 1 - - - + + + - 2 - - - + + + - 3 - - - + + + - 4 - - - + + + - 5 - - - + + + - 6 - - - + + + - 7 - - - - + + + + - 8 - - - + + + - 9 - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/data/toolsQuick.xml b/ui/data/toolsQuick.xml new file mode 100644 --- /dev/null +++ b/ui/data/toolsQuick.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/pageview.h b/ui/pageview.h --- a/ui/pageview.h +++ b/ui/pageview.h @@ -30,6 +30,7 @@ #include "core/observer.h" #include "core/view.h" +class QMenu; class KActionCollection; namespace Okular { @@ -250,13 +251,12 @@ void slotAutoFitToggled( bool ); void slotViewMode( QAction *action ); void slotContinuousToggled( bool ); - void slotSetMouseNormal(); + void slotMouseNormalToggled( bool ); void slotSetMouseZoom(); void slotSetMouseMagnifier(); void slotSetMouseSelect(); void slotSetMouseTextSelect(); void slotSetMouseTableSelect(); - void slotToggleAnnotator( bool ); void slotAutoScrollUp(); void slotAutoScrollDown(); void slotScrollUp( bool singleStep = false ); diff --git a/ui/pageview.cpp b/ui/pageview.cpp --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -74,7 +74,7 @@ #include "pageviewannotator.h" #include "pageviewmouseannotation.h" #include "priorities.h" -#include "toolaction.h" +#include "toggleactionmenu.h" #include "okmenutitle.h" #ifdef HAVE_SPEECH #include "tts.h" @@ -226,7 +226,6 @@ QAction * aMouseTableSelect; QAction * aMouseMagnifier; KToggleAction * aTrimToSelection; - KToggleAction * aToggleAnnotator; KSelectAction * aZoom; QAction * aZoomIn; QAction * aZoomOut; @@ -244,6 +243,7 @@ QAction * aSpeakPauseResume; KActionCollection * actionCollection; QActionGroup * mouseModeActionGroup; + ToggleActionMenu * aMouseModeMenu; QAction * aFitWindowToPage; int setting_viewCols; @@ -375,7 +375,6 @@ d->aMouseNormal = nullptr; d->aMouseSelect = nullptr; d->aMouseTextSelect = nullptr; - d->aToggleAnnotator = nullptr; d->aZoomFitWidth = nullptr; d->aZoomFitPage = nullptr; d->aZoomAutoFit = nullptr; @@ -624,7 +623,7 @@ d->mouseModeActionGroup->setExclusive( true ); d->aMouseNormal = new QAction( QIcon::fromTheme( QStringLiteral("transform-browse") ), i18n( "&Browse" ), this ); ac->addAction(QStringLiteral("mouse_drag"), d->aMouseNormal ); - connect( d->aMouseNormal, &QAction::triggered, this, &PageView::slotSetMouseNormal ); + connect( d->aMouseNormal, &QAction::toggled, this, &PageView::slotMouseNormalToggled ); d->aMouseNormal->setCheckable( true ); ac->setDefaultShortcut(d->aMouseNormal, QKeySequence(Qt::CTRL + Qt::Key_1)); d->aMouseNormal->setActionGroup( d->mouseModeActionGroup ); @@ -685,17 +684,16 @@ d->aMouseMagnifier->setActionGroup( d->mouseModeActionGroup ); d->aMouseMagnifier->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier ); - d->aToggleAnnotator = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-freehand") ), i18n("&Review"), this); - ac->addAction(QStringLiteral("mouse_toggle_annotate"), d->aToggleAnnotator ); - d->aToggleAnnotator->setCheckable( true ); - connect( d->aToggleAnnotator, &QAction::toggled, this, &PageView::slotToggleAnnotator ); - ac->setDefaultShortcut(d->aToggleAnnotator, Qt::Key_F6); - - ToolAction *ta = new ToolAction( this ); - ac->addAction( QStringLiteral("mouse_selecttools"), ta ); - ta->addAction( d->aMouseTextSelect ); - ta->addAction( d->aMouseSelect ); - ta->addAction( d->aMouseTableSelect ); + // Mouse-Mode action menu + d->aMouseModeMenu = new ToggleActionMenu( QIcon(),QString(), this, + QToolButton::MenuButtonPopup, + ToggleActionMenu::ImplicitDefaultAction ); + d->aMouseModeMenu->addAction( d->aMouseSelect ); + d->aMouseModeMenu->addAction( d->aMouseTextSelect ); + d->aMouseModeMenu->addAction( d->aMouseTableSelect ); + d->aMouseModeMenu->suggestDefaultAction( d->aMouseTextSelect ); + d->aMouseModeMenu->setText( i18nc( "@action", "Selection Tools" ) ); + ac->addAction( QStringLiteral( "mouse_selecttools" ), d->aMouseModeMenu ); // speak actions #ifdef HAVE_SPEECH @@ -763,6 +761,13 @@ connect(d->document, &Okular::Document::canRedoChanged, kredo, &QAction::setEnabled); kundo->setEnabled(false); kredo->setEnabled(false); + + if( !d->annotator ) + { + d->annotator = new PageViewAnnotator( this, d->document ); + connect( d->annotator, &PageViewAnnotator::toolSelected, d->aMouseNormal , &QAction::trigger ); + } + d->annotator->setupActions( ac ); } bool PageView::canFitPageWidth() const @@ -878,12 +883,7 @@ updatePageStep(); if ( d->annotator ) - { - d->annotator->setEnabled( false ); d->annotator->reparseConfig(); - if ( d->aToggleAnnotator->isChecked() ) - slotToggleAnnotator( true ); - } // Something like invert colors may have changed // As we don't have a way to find out the old value @@ -1016,13 +1016,8 @@ void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setupFlags ) { bool documentChanged = setupFlags & Okular::DocumentObserver::DocumentChanged; - const bool allownotes = d->document->isAllowed( Okular::AllowNotes ); const bool allowfillforms = d->document->isAllowed( Okular::AllowFillForms ); - // allownotes may have changed - if ( d->aToggleAnnotator ) - d->aToggleAnnotator->setEnabled( allownotes ); - // reuse current pages if nothing new if ( ( pageSet.count() == d->items.count() ) && !documentChanged && !( setupFlags & Okular::DocumentObserver::NewLayoutForPages ) ) { @@ -1235,6 +1230,8 @@ if ( d->mouseModeActionGroup ) d->mouseModeActionGroup->setEnabled( haspages ); + if ( d->aMouseModeMenu ) + d->aMouseModeMenu->setEnabled( haspages ); if ( d->aRotateClockwise ) d->aRotateClockwise->setEnabled( haspages ); @@ -1253,14 +1250,6 @@ d->annotator->setToolsEnabled( allowTools ); d->annotator->setTextToolsEnabled( allowTools && d->document->supportsSearching() ); } - if ( d->aToggleAnnotator ) - { - if ( !allowAnnotations && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - } - d->aToggleAnnotator->setEnabled( allowAnnotations ); - } #ifdef HAVE_SPEECH if ( d->aSpeakDoc ) { @@ -5166,32 +5155,30 @@ } } -void PageView::slotSetMouseNormal() +void PageView::slotMouseNormalToggled( bool checked ) { - d->mouseMode = Okular::Settings::EnumMouseMode::Browse; - Okular::Settings::setMouseMode( d->mouseMode ); - // hide the messageWindow - d->messageWindow->hide(); - // reshow the annotator toolbar if hiding was forced (and if it is not already visible) - if ( d->annotator && d->annotator->hidingWasForced() && d->aToggleAnnotator && !d->aToggleAnnotator->isChecked() ) - d->aToggleAnnotator->trigger(); - // force an update of the cursor - updateCursor(); - Okular::Settings::self()->save(); + if ( checked ) + { + d->mouseMode = Okular::Settings::EnumMouseMode::Browse; + Okular::Settings::setMouseMode( d->mouseMode ); + // hide the messageWindow + d->messageWindow->hide(); + // force an update of the cursor + updateCursor(); + Okular::Settings::self()->save(); + } + else + { + d->annotator->detachAnnotation(); + } } void PageView::slotSetMouseZoom() { d->mouseMode = Okular::Settings::EnumMouseMode::Zoom; Okular::Settings::setMouseMode( d->mouseMode ); // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Select zooming area. Right-click to zoom out." ), QString(), PageViewMessage::Info, -1 ); - // force hiding of annotator toolbar - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); @@ -5214,12 +5201,6 @@ Okular::Settings::setMouseMode( d->mouseMode ); // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Draw a rectangle around the text/graphics to copy." ), QString(), PageViewMessage::Info, -1 ); - // force hiding of annotator toolbar - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); @@ -5231,12 +5212,6 @@ Okular::Settings::setMouseMode( d->mouseMode ); // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Select text" ), QString(), PageViewMessage::Info, -1 ); - // force hiding of annotator toolbar - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); @@ -5250,75 +5225,11 @@ d->messageWindow->display( i18n( "Draw a rectangle around the table, then click near edges to divide up; press Esc to clear." ), QString(), PageViewMessage::Info, -1 ); - // force hiding of annotator toolbar - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); Okular::Settings::self()->save(); } -void PageView::slotToggleAnnotator( bool on ) -{ - // the 'inHere' trick is needed as the slotSetMouseZoom() calls this - static bool inHere = false; - if ( inHere ) - return; - inHere = true; - - // the annotator can be used in normal mouse mode only, so if asked for it, - // switch to normal mode - if ( on && d->mouseMode != Okular::Settings::EnumMouseMode::Browse ) - d->aMouseNormal->trigger(); - - // ask for Author's name if not already set - if ( Okular::Settings::identityAuthor().isEmpty() ) - { - // get default username from the kdelibs/kdecore/KUser - KUser currentUser; - QString userName = currentUser.property( KUser::FullName ).toString(); - // ask the user for confirmation/change - if ( userName.isEmpty() ) - { - bool ok = false; - userName = QInputDialog::getText(nullptr, - i18n( "Annotations author" ), - i18n( "Please insert your name or initials:" ), - QLineEdit::Normal, - QString(), - &ok ); - - if ( !ok ) - { - d->aToggleAnnotator->trigger(); - inHere = false; - return; - } - } - // save the name - Okular::Settings::setIdentityAuthor( userName ); - Okular::Settings::self()->save(); - } - - // create the annotator object if not present - if ( !d->annotator ) - { - d->annotator = new PageViewAnnotator( this, d->document ); - bool allowTools = d->document->pages() > 0 && d->document->isAllowed( Okular::AllowNotes ); - d->annotator->setToolsEnabled( allowTools ); - d->annotator->setTextToolsEnabled( allowTools && d->document->supportsSearching() ); - } - - // initialize/reset annotator (and show/hide toolbar) - d->annotator->setEnabled( on ); - d->annotator->setHidingForced( false ); - - inHere = false; -} - void PageView::slotAutoScrollUp() { if ( d->scrollIncrement < -9 ) @@ -5458,12 +5369,6 @@ d->mouseMode = Okular::Settings::EnumMouseMode::TrimSelect; // change the text in messageWindow (and show it if hidden) d->messageWindow->display( i18n( "Draw a rectangle around the page area you wish to keep visible" ), QString(), PageViewMessage::Info, -1 ); - // force hiding of annotator toolbar - if ( d->aToggleAnnotator && d->aToggleAnnotator->isChecked() ) - { - d->aToggleAnnotator->trigger(); - d->annotator->setHidingForced( true ); - } // force an update of the cursor updateCursor(); } else { diff --git a/ui/pageviewannotator.h b/ui/pageviewannotator.h --- a/ui/pageviewannotator.h +++ b/ui/pageviewannotator.h @@ -14,20 +14,24 @@ #include #include +#include + #include "pageviewutils.h" #include "annotationtools.h" class QKeyEvent; class QMouseEvent; class QPainter; +class AnnotationActionHandler; namespace Okular { class Document; } // engines are defined and implemented in the cpp class AnnotatorEngine; +class AnnotationTools; class PageView; /** @@ -40,31 +44,26 @@ * to this class that performs a rough visual representation of what the * annotation will become when finished. * - * m_toolsDefinition is a DOM object that contains Annotations/Engine association - * for the items placed in the toolbar. The XML is parsed (1) when populating - * the toolbar and (2)after selecting a toolbar item, in which case an Ann is + * m_toolsDefinition is a AnnotationTools object that wraps a DOM object that + * contains Annotations/Engine association for the items placed in the toolbar. + * The XML is parsed after selecting a toolbar item, in which case an Ann is * initialized with the values in the XML and an engine is created to handle * that annotation. m_toolsDefinition is created in reparseConfig according to - * user configuration. + * user configuration. m_toolsDefinition is updated (and saved to disk) (1) each + * time a property of an annotation (color, font, etc) is changed by the user, + * and (2) each time a "quick annotation" is selected, in which case the properties + * of the selected quick annotation are written over those of the corresponding + * builtin tool */ class PageViewAnnotator : public QObject { Q_OBJECT public: + static const int STAMP_TOOL_ID; + PageViewAnnotator( PageView * parent, Okular::Document * storage ); ~PageViewAnnotator(); - // called to show/hide the editing toolbar - void setEnabled( bool enabled ); - - // called to toggle the usage of text annotating tools - void setTextToolsEnabled( bool enabled ); - - void setToolsEnabled( bool enabled ); - - void setHidingForced( bool forced ); - bool hidingWasForced() const; - // methods used when creating the annotation // @return Is a tool currently selected? bool active() const; @@ -88,25 +87,62 @@ static QString defaultToolName( const QDomElement &toolElement ); static QPixmap makeToolPixmap( const QDomElement &toolElement ); - private Q_SLOTS: - void slotToolSelected( int toolID ); - void slotSaveToolbarOrientation( int side ); - void slotToolDoubleClicked( int toolID ); + // methods related to the annotation actions + void setupActions( KActionCollection *ac ); + // @return Is continuous mode active (pin annotation)? + bool continuousMode(); + // enable/disable the annotation actions + void setToolsEnabled( bool enabled ); + // enable/disable the text-selection annotation actions + void setTextToolsEnabled( bool enabled ); - private: + // selects the active tool + void selectTool( int toolID ); + // selects a stamp tool and sets the stamp symbol + void selectStampTool( const QString &stampSymbol ); + // makes a quick annotation the active tool + int setQuickTool ( int toolID ); + // deselects the tool and uncheck all the annotation actions void detachAnnotation(); + // returns the builtin annotation tool with the given Id + QDomElement builtinTool( int toolID ); + // returns the quick annotation tool with the given Id + QDomElement quickTool( int toolID ); + + // methods that write the properties + void setAnnotationWidth( double width ); + void setAnnotationColor( const QColor &color ); + void setAnnotationInnerColor( const QColor &color ); + void setAnnotationOpacity( double opacity ); + void setAnnotationFont( const QFont &font ); + + public Q_SLOTS: + void setContinuousMode( bool enabled ); + void addToQuickAnnotations(); + void slotAdvancedSettings(); + + Q_SIGNALS: + void toolSelected(); + + private: + // save the annotation tools to Okular settings + void saveAnnotationTools(); + // returns the engine QDomElement of the the currently active tool + QDomElement currentEngineElement(); + // returns the annotation QDomElement of the the currently active tool + QDomElement currentAnnotationElement(); + // global class pointers Okular::Document * m_document; PageView * m_pageView; - PageViewToolBar * m_toolBar; + AnnotationActionHandler * m_actionHandler; AnnotatorEngine * m_engine; - QDomElement m_toolsDefinition; - QLinkedList m_items; + AnnotationTools * m_toolsDefinition; + AnnotationTools * m_quickToolsDefinition; bool m_textToolsEnabled; bool m_toolsEnabled; bool m_continuousMode; - bool m_hidingWasForced; // creation related variables int m_lastToolID; diff --git a/ui/pageviewannotator.cpp b/ui/pageviewannotator.cpp --- a/ui/pageviewannotator.cpp +++ b/ui/pageviewannotator.cpp @@ -33,10 +33,12 @@ #include // local includes +#include "conf/editannottooldialog.h" #include "core/area.h" #include "core/document.h" #include "core/page.h" #include "core/annotations.h" +#include "ui/annotationactionhandler.h" #include "settings.h" #include "annotationtools.h" #include "guiutils.h" @@ -656,134 +658,119 @@ QRect rect; }; - -/** PageViewAnnotator **/ - -PageViewAnnotator::PageViewAnnotator( PageView * parent, Okular::Document * storage ) - : QObject( parent ), m_document( storage ), m_pageView( parent ), - m_toolBar( nullptr ), m_engine( nullptr ), m_textToolsEnabled( false ), m_toolsEnabled( false ), - m_continuousMode( false ), m_hidingWasForced( false ), m_lastToolID( -1 ), m_lockedItem( nullptr ) -{ - reparseConfig(); -} - -void PageViewAnnotator::reparseConfig() +/** @short AnnotationTools*/ +class AnnotationTools { - m_items.clear(); - - // Read tool list from configuration. It's a list of XML elements - const QStringList userTools = Okular::Settings::annotationTools(); + public: + AnnotationTools() : m_toolsCount( 0 ) {} - // Populate m_toolsDefinition - QDomDocument doc; - m_toolsDefinition = doc.createElement( QStringLiteral("annotatingTools") ); - foreach ( const QString &toolXml, userTools ) - { - QDomDocument entryParser; - if ( entryParser.setContent( toolXml ) ) - m_toolsDefinition.appendChild( doc.importNode( entryParser.documentElement(), true ) ); - else - qCWarning(OkularUiDebug) << "Skipping malformed tool XML in AnnotationTools setting"; - } + void setTools( QStringList tools ) + { + // Populate m_toolsDefinition + m_toolsCount = 0; + m_toolsDefinition.clear(); + QDomElement root = m_toolsDefinition.createElement( QStringLiteral( "root" ) ); + m_toolsDefinition.appendChild( root ); + foreach ( const QString &toolXml, tools ) + { + QDomDocument entryParser; + if ( entryParser.setContent( toolXml ) ) { + root.appendChild( m_toolsDefinition.importNode( entryParser.documentElement(), true ) ); + m_toolsCount++; + } else { + qCWarning(OkularUiDebug) << "Skipping malformed tool XML in AnnotationTools setting"; + } + } + } - // Create the AnnotationToolItems from the XML dom tree - QDomNode toolDescription = m_toolsDefinition.firstChild(); - while ( toolDescription.isElement() ) - { - QDomElement toolElement = toolDescription.toElement(); - if ( toolElement.tagName() == QLatin1String("tool") ) + QStringList toStringList() { - AnnotationToolItem item; - item.id = toolElement.attribute(QStringLiteral("id")).toInt(); - if ( toolElement.hasAttribute( QStringLiteral("name") ) ) - item.text = toolElement.attribute( QStringLiteral("name") ); - else - item.text = defaultToolName( toolElement ); - item.pixmap = makeToolPixmap( toolElement ); - QDomNode shortcutNode = toolElement.elementsByTagName( QStringLiteral("shortcut") ).item( 0 ); - if ( shortcutNode.isElement() ) - item.shortcut = shortcutNode.toElement().text(); - QDomNodeList engineNodeList = toolElement.elementsByTagName( QStringLiteral("engine") ); - if ( engineNodeList.size() > 0 ) + QStringList tools; + QDomElement toolElement = m_toolsDefinition.documentElement().firstChildElement(); + QString str; + QTextStream stream(&str); + while( !toolElement.isNull() ) { - QDomElement engineEl = engineNodeList.item( 0 ).toElement(); - if ( !engineEl.isNull() && engineEl.hasAttribute( QStringLiteral("type") ) ) - item.isText = engineEl.attribute( QStringLiteral("type") ) == QLatin1String( "TextSelector" ); + str.clear(); + toolElement.save(stream, -1 /* indent disabled */); + tools << str; + toolElement = toolElement.nextSiblingElement(); } - m_items.push_back( item ); + return tools; } - toolDescription = toolDescription.nextSibling(); - } -} -PageViewAnnotator::~PageViewAnnotator() -{ - delete m_engine; -} + QDomElement tool( int toolID ) + { + QDomElement toolElement = m_toolsDefinition.documentElement().firstChildElement(); + while( !toolElement.isNull() && toolElement.attribute("id").toInt() != toolID ) { + toolElement = toolElement.nextSiblingElement(); + } + return toolElement; // can return a null element + } -void PageViewAnnotator::setEnabled( bool on ) -{ - if ( !on ) - { - // remove toolBar - if ( m_toolBar ) - m_toolBar->hideAndDestroy(); - m_toolBar = nullptr; - // deactivate the active tool, if any - slotToolSelected( -1 ); - return; - } + void appendTool( QDomElement toolElement ) + { + toolElement = toolElement.cloneNode().toElement(); + toolElement.setAttribute( QStringLiteral( "id" ), ++m_toolsCount ); + m_toolsDefinition.documentElement().appendChild( toolElement ); + } - // if no tools are defined, don't show the toolbar - if ( !m_toolsDefinition.hasChildNodes() ) - return; + bool updateTool( QDomElement newToolElement, int toolID ) + { + QDomElement toolElement = tool( toolID ); + if ( toolElement.isNull() ) + return false; + newToolElement = newToolElement.cloneNode().toElement(); + newToolElement.setAttribute( QStringLiteral( "id" ), toolID ); + QDomNode oldTool = m_toolsDefinition.documentElement().replaceChild( newToolElement, toolElement ); + return !oldTool.isNull(); + } - // create toolBar - if ( !m_toolBar ) - { - m_toolBar = new PageViewToolBar( m_pageView, m_pageView->viewport() ); - m_toolBar->setSide( (PageViewToolBar::Side)Okular::Settings::editToolBarPlacement() ); - m_toolBar->setItems( m_items ); - m_toolBar->setToolsEnabled( m_toolsEnabled ); - m_toolBar->setTextToolsEnabled( m_textToolsEnabled ); - connect(m_toolBar, &PageViewToolBar::toolSelected, this, &PageViewAnnotator::slotToolSelected); - connect(m_toolBar, &PageViewToolBar::orientationChanged, this, &PageViewAnnotator::slotSaveToolbarOrientation); - - connect(m_toolBar, &PageViewToolBar::buttonDoubleClicked, this, &PageViewAnnotator::slotToolDoubleClicked); - m_toolBar->setCursor(Qt::ArrowCursor); - } + private: + QDomDocument m_toolsDefinition; + int m_toolsCount; +}; - // show the toolBar - m_toolBar->showAndAnimate(); -} +/** PageViewAnnotator **/ +const int PageViewAnnotator::STAMP_TOOL_ID = 14; -void PageViewAnnotator::setTextToolsEnabled( bool enabled ) +PageViewAnnotator::PageViewAnnotator( PageView * parent, Okular::Document * storage ) + : QObject( parent ), m_document( storage ), m_pageView( parent ), + m_actionHandler( nullptr ), m_engine( nullptr ), m_toolsDefinition( nullptr ), + m_quickToolsDefinition( nullptr ), m_continuousMode( true ), m_lastToolID( -1 ), + m_lockedItem( nullptr ) { - m_textToolsEnabled = enabled; - if ( m_toolBar ) - m_toolBar->setTextToolsEnabled( m_textToolsEnabled ); + reparseConfig(); } -void PageViewAnnotator::setToolsEnabled( bool enabled ) +void PageViewAnnotator::reparseConfig() { - m_toolsEnabled = enabled; - if ( m_toolBar ) - m_toolBar->setToolsEnabled( m_toolsEnabled ); -} + // Read tool list from configuration. It's a list of XML elements + if ( !m_toolsDefinition ) + m_toolsDefinition = new AnnotationTools(); + m_toolsDefinition->setTools( Okular::Settings::annotationTools() ); -void PageViewAnnotator::setHidingForced( bool forced ) -{ - m_hidingWasForced = forced; + if ( !m_quickToolsDefinition ) + m_quickToolsDefinition = new AnnotationTools(); + m_quickToolsDefinition->setTools( Okular::Settings::quickAnnotationTools() ); + + m_continuousMode = Okular::Settings::annotationContinuousMode(); + + if ( Okular::Settings::identityAuthor().isEmpty() ) + detachAnnotation(); + + if ( m_actionHandler ) + m_actionHandler->reparseTools(); } -bool PageViewAnnotator::hidingWasForced() const +PageViewAnnotator::~PageViewAnnotator() { - return m_hidingWasForced; + delete m_engine; } bool PageViewAnnotator::active() const { - return m_engine && m_toolBar; + return m_engine != nullptr; } bool PageViewAnnotator::annotating() const @@ -866,7 +853,7 @@ } if ( m_continuousMode ) - slotToolSelected( m_lastToolID ); + selectTool( m_lastToolID ); else detachAnnotation(); } @@ -895,18 +882,6 @@ return QRect(); } - // We set all tablet events that take place over the annotations toolbar to ignore so that corresponding mouse - // events will be delivered to the toolbar. However, we still allow the annotations code to handle - // TabletMove and TabletRelease events in case the user is drawing an annotation onto the toolbar. - const QPoint toolBarPos = m_toolBar->mapFromGlobal( e->globalPos() ); - const QRect toolBarRect = m_toolBar->rect(); - if ( toolBarRect.contains( toolBarPos ) ) - { - e->ignore(); - if (e->type() == QEvent::TabletPress) - return QRect(); - } - AnnotatorEngine::EventType eventType; AnnotatorEngine::Button button; @@ -930,7 +905,7 @@ bool PageViewAnnotator::routePaints( const QRect & wantedRect ) const { - return m_engine && m_toolBar && wantedRect.intersects( m_lastDrawnRect ) && m_lockedItem; + return m_engine && wantedRect.intersects( m_lastDrawnRect ) && m_lockedItem; } void PageViewAnnotator::routePaint( QPainter * painter, const QRect & paintRect ) @@ -959,8 +934,38 @@ painter->restore(); } -void PageViewAnnotator::slotToolSelected( int toolID ) +void PageViewAnnotator::selectTool( int toolID ) { + + // ask for Author's name if not already set + if ( toolID > 0 && Okular::Settings::identityAuthor().isEmpty() ) + { + // get default username from the kdelibs/kdecore/KUser + KUser currentUser; + QString userName = currentUser.property( KUser::FullName ).toString(); + userName = ""; + // ask the user for confirmation/change + if ( userName.isEmpty() ) + { + bool ok = false; + userName = QInputDialog::getText(nullptr, + i18n( "Bookmark annotation" ), + i18n( "Insert a custom name for the annotation:" ), + QLineEdit::Normal, + QString(), + &ok ); + + if ( !ok ) + { + detachAnnotation(); + return; + } + } + // save the name + Okular::Settings::setIdentityAuthor( userName ); + Okular::Settings::self()->save(); + } + // terminate any previous operation if ( m_engine ) { @@ -974,7 +979,6 @@ m_lastDrawnRect = QRect(); } - if ( toolID != m_lastToolID ) m_continuousMode = false; // store current tool for later usage m_lastToolID = toolID; @@ -987,38 +991,25 @@ } // for the selected tool create the Engine - QDomNode toolNode = m_toolsDefinition.firstChild(); - while ( toolNode.isElement() ) + QDomElement toolElement = m_toolsDefinition->tool( toolID ); + if ( !toolElement.isNull() ) { - QDomElement toolElement = toolNode.toElement(); - toolNode = toolNode.nextSibling(); - - // only find out the element describing selected tool - if ( toolElement.tagName() != QLatin1String("tool") || toolElement.attribute(QStringLiteral("id")).toInt() != toolID ) - continue; - // parse tool properties - QDomNode toolSubNode = toolElement.firstChild(); - while ( toolSubNode.isElement() ) + QDomElement engineElement = toolElement.firstChildElement( QStringLiteral("engine") ); + if ( !engineElement.isNull() ) { - QDomElement toolSubElement = toolSubNode.toElement(); - toolSubNode = toolSubNode.nextSibling(); - // create the AnnotatorEngine - if ( toolSubElement.tagName() == QLatin1String("engine") ) - { - QString type = toolSubElement.attribute( QStringLiteral("type") ); - if ( type == QLatin1String("SmoothLine") ) - m_engine = new SmoothPathEngine( toolSubElement ); - else if ( type == QLatin1String("PickPoint") ) - m_engine = new PickPointEngine( toolSubElement ); - else if ( type == QLatin1String("PolyLine") ) - m_engine = new PolyLineEngine( toolSubElement ); - else if ( type == QLatin1String("TextSelector") ) - m_engine = new TextSelectorEngine( toolSubElement, m_pageView ); - else - qCWarning(OkularUiDebug).nospace() << "tools.xml: engine type:'" << type << "' is not defined!"; - } + QString type = engineElement.attribute( QStringLiteral("type") ); + if ( type == QLatin1String("SmoothLine") ) + m_engine = new SmoothPathEngine( engineElement ); + else if ( type == QLatin1String("PickPoint") ) + m_engine = new PickPointEngine( engineElement ); + else if ( type == QLatin1String("PolyLine") ) + m_engine = new PolyLineEngine( engineElement ); + else if ( type == QLatin1String("TextSelector") ) + m_engine = new TextSelectorEngine( engineElement, m_pageView ); + else + qCWarning(OkularUiDebug).nospace() << "tools.xml: engine type:'" << type << "' is not defined!"; // display the tooltip const QString annotType = toolElement.attribute( QStringLiteral("type") ); @@ -1062,25 +1053,28 @@ } m_pageView->updateCursor(); - // stop after parsing selected tool's node - break; } -} -void PageViewAnnotator::slotSaveToolbarOrientation( int side ) -{ - Okular::Settings::setEditToolBarPlacement( (int)side ); - Okular::Settings::self()->save(); + if ( toolID > 0 ) + emit toolSelected(); } -void PageViewAnnotator::slotToolDoubleClicked( int /*toolID*/ ) +void PageViewAnnotator::selectStampTool( const QString &stampSymbol) { - m_continuousMode = true; + QDomElement toolElement = builtinTool( STAMP_TOOL_ID ); + QDomElement engineElement = toolElement.firstChildElement( QStringLiteral("engine") ); + QDomElement annotationElement = engineElement.firstChildElement( QStringLiteral("annotation") ); + engineElement.setAttribute( QStringLiteral( "hoverIcon" ), stampSymbol ); + annotationElement.setAttribute( QStringLiteral( "icon" ), stampSymbol ); + saveAnnotationTools(); + selectTool( STAMP_TOOL_ID ); } void PageViewAnnotator::detachAnnotation() { - m_toolBar->selectButton( -1 ); + selectTool( -1 ); + if ( m_actionHandler ) + m_actionHandler->deselectAllAnnotationActions(); } QString PageViewAnnotator::defaultToolName( const QDomElement &toolElement ) @@ -1278,6 +1272,166 @@ return pixmap; } +void PageViewAnnotator::setupActions( KActionCollection * ac ) +{ + if ( !m_actionHandler ) { + m_actionHandler = new AnnotationActionHandler( this, ac ); + } +} + +bool PageViewAnnotator::continuousMode() +{ + return m_continuousMode; +} + +void PageViewAnnotator::setContinuousMode( bool enabled ) +{ + m_continuousMode = enabled; + Okular::Settings::setAnnotationContinuousMode( enabled ); + Okular::Settings::self()->save(); +} + +void PageViewAnnotator::setToolsEnabled( bool enabled ) +{ + if ( m_actionHandler ) + m_actionHandler->setToolsEnabled( enabled ); +} + +void PageViewAnnotator::setTextToolsEnabled( bool enabled ) +{ + if ( m_actionHandler ) + m_actionHandler->setTextToolsEnabled( enabled ); +} + +void PageViewAnnotator::saveAnnotationTools() +{ + Okular::Settings::setAnnotationTools( m_toolsDefinition->toStringList() ); + Okular::Settings::setQuickAnnotationTools( m_quickToolsDefinition->toStringList() ); + Okular::Settings::self()->save(); +} + +int PageViewAnnotator::setQuickTool( int favToolID ) +{ + int toolId = -1; + QDomElement favToolElement = m_quickToolsDefinition->tool( favToolID ); + if ( !favToolElement.isNull() && favToolElement.hasAttribute( QStringLiteral("sourceId") ) ) + { + toolId = favToolElement.attribute( QStringLiteral("sourceId") ).toInt(); + if ( m_toolsDefinition->updateTool( favToolElement, toolId ) ) + saveAnnotationTools(); + } + return toolId; +} + +QDomElement PageViewAnnotator::builtinTool( int toolID ) +{ + return m_toolsDefinition->tool( toolID ); +} + +QDomElement PageViewAnnotator::quickTool( int toolID ) +{ + return m_quickToolsDefinition->tool( toolID ); +} + +QDomElement PageViewAnnotator::currentEngineElement() +{ + return m_toolsDefinition->tool( m_lastToolID ).firstChildElement( QStringLiteral("engine") ); +} + +QDomElement PageViewAnnotator::currentAnnotationElement() +{ + return currentEngineElement().firstChildElement( QStringLiteral("annotation") ); +} + +void PageViewAnnotator::setAnnotationWidth( double width ) +{ + currentAnnotationElement().setAttribute( QStringLiteral( "width" ), QString::number( width ) ); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationColor( const QColor &color ) +{ + currentEngineElement().setAttribute( QStringLiteral( "color" ), color.name(QColor::HexRgb ) ); + QDomElement annotationElement = currentAnnotationElement(); + QString annotType = annotationElement.attribute( QStringLiteral( "type" ) ); + if ( annotType == "Typewriter" ) { + annotationElement.setAttribute( QStringLiteral( "textColor" ), color.name( QColor::HexRgb ) ); + } else { + annotationElement.setAttribute( QStringLiteral( "color" ), color.name( QColor::HexRgb ) ); + } + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationInnerColor( const QColor &color ) +{ + QDomElement annotationElement = currentAnnotationElement(); + QString annotType = annotationElement.attribute( QStringLiteral( "type" ) ); + if ( color == Qt::transparent ) { + annotationElement.removeAttribute( QStringLiteral( "innerColor" ) ); + } else { + annotationElement.setAttribute( QStringLiteral( "innerColor" ), color.name( QColor::HexRgb ) ); + } + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationOpacity( double opacity ) +{ + currentAnnotationElement().setAttribute( QStringLiteral( "opacity" ), QString::number( opacity ) ); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::setAnnotationFont( const QFont &font ) +{ + currentAnnotationElement().setAttribute( QStringLiteral( "font" ), font.toString() ); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + +void PageViewAnnotator::addToQuickAnnotations() +{ + QDomElement sourceToolElement = m_toolsDefinition->tool( m_lastToolID ); + if ( sourceToolElement.isNull() ) + return; + + // set custom name for quick annotation + bool ok = false; + QString itemText = QInputDialog::getText(nullptr, + i18n( "Add favorite annotation" ), + i18n( "Custom annotation name:" ), + QLineEdit::Normal, + defaultToolName( sourceToolElement ), + &ok ); + if ( !ok ) + return; + + QDomElement toolElement = sourceToolElement.cloneNode().toElement(); + // store name attribute only if the user specified a customized name + if ( !itemText.isEmpty() ) + toolElement.setAttribute( QStringLiteral("name"), itemText ); + toolElement.setAttribute( QStringLiteral("sourceId"), sourceToolElement.attribute( QStringLiteral("id") ) ); + m_quickToolsDefinition->appendTool( toolElement ); + saveAnnotationTools(); +} + +void PageViewAnnotator::slotAdvancedSettings() +{ + QDomElement toolElement = m_toolsDefinition->tool( m_lastToolID ); + + EditAnnotToolDialog t( nullptr, toolElement, true ); + if ( t.exec() != QDialog::Accepted ) + return; + + QDomElement toolElementUpdated = t.toolXml().documentElement(); + int toolID = toolElement.attribute( QStringLiteral("id") ).toInt(); + m_toolsDefinition->updateTool( toolElementUpdated, toolID ); + saveAnnotationTools(); + selectTool( m_lastToolID ); +} + #include "moc_pageviewannotator.cpp" /* kate: replace-tabs on; indent-width 4; */ diff --git a/ui/pageviewutils.h b/ui/pageviewutils.h --- a/ui/pageviewutils.h +++ b/ui/pageviewutils.h @@ -17,8 +17,6 @@ #include #include #include -#include - #include "core/area.h" @@ -128,97 +126,4 @@ int m_lineSpacing; }; -struct AnnotationToolItem -{ - AnnotationToolItem() - : id( -1 ), isText( false ) - { - } - - int id; - QString text; - QPixmap pixmap; - QString shortcut; - bool isText; -}; - -class ToolBarButton : public QToolButton -{ - Q_OBJECT - public: - static const int iconSize = 32; - static const int buttonSize = 40; - - ToolBarButton( QWidget * parent, const AnnotationToolItem &item ); - int buttonID() const { return m_id; } - bool isText() const { return m_isText; } - - Q_SIGNALS: - void buttonDoubleClicked( int buttonID ); - - protected: - void mouseDoubleClickEvent( QMouseEvent * event ) override; - - private: - int m_id; - bool m_isText; -}; - -/** - * @short A widget containing exclusive buttons, that slides in from a side. - * - * This is a shaped widget that slides in from a side of the 'anchor widget' - * it's attached to. It can be dragged and docked on {left,top,right,bottom} - * sides and contains toggable exclusive buttons. - * When a 'tool' of this 'toolBar' is selected, a signal is emitted. - */ -class PageViewToolBar : public QWidget -{ - Q_OBJECT - public: - PageViewToolBar( PageView * parent, QWidget * anchorWidget ); - ~PageViewToolBar(); - - // animated widget controls - enum Side { Left = 0, Top = 1, Right = 2, Bottom = 3 }; - - void setItems( const QLinkedList &items ); - void setSide( Side side ); - - void showAndAnimate(); - void hideAndDestroy(); - - void selectButton( int id ); - - void setToolsEnabled( bool on ); - void setTextToolsEnabled( bool on ); - - // query properties - - Q_SIGNALS: - // the tool 'toolID' has been selected - void toolSelected( int toolID ); - // orientation has been changed - void orientationChanged( int side ); - // a tool button of this toolbar has been double clicked - void buttonDoubleClicked( int buttonID ); - - protected: - // handle widget events { anchor_resize, paint, animation, drag } - bool eventFilter( QObject * o, QEvent * e ) override; - void paintEvent( QPaintEvent * ) override; - void mousePressEvent( QMouseEvent * e ) override; - void mouseMoveEvent( QMouseEvent * e ) override; - void mouseReleaseEvent( QMouseEvent * e ) override; - - private: - // private variables - friend class ToolBarPrivate; - class ToolBarPrivate * d; - - private Q_SLOTS: - void slotAnimate(); - void slotButtonClicked(); -}; - #endif diff --git a/ui/pageviewutils.cpp b/ui/pageviewutils.cpp --- a/ui/pageviewutils.cpp +++ b/ui/pageviewutils.cpp @@ -14,30 +14,14 @@ // qt/kde includes #include -#include -#include -#include -#include -#include +#include #include -#include -#include #include -#include -#include -#include -#include #include -#include - -// system includes -#include // local includes #include "formwidgets.h" -#include "pageview.h" #include "videowidget.h" -#include "core/movie.h" #include "core/page.h" #include "core/form.h" #include "settings.h" @@ -429,520 +413,4 @@ hide(); } - -/*********************/ -/** PageViewToolBar */ -/*********************/ - -ToolBarButton::ToolBarButton( QWidget * parent, const AnnotationToolItem &item ) - : QToolButton( parent ), m_id( item.id ), m_isText( item.isText ) -{ - setCheckable( true ); - setAutoRaise( true ); - resize( buttonSize, buttonSize ); - setIconSize( QSize( iconSize, iconSize ) ); - setIcon( QIcon( item.pixmap ) ); - // set shortcut if defined - if ( !item.shortcut.isEmpty() ) - setShortcut( QKeySequence( item.shortcut ) ); - else - KAcceleratorManager::setNoAccel( this ); - - // if accel is set display it along name - QString accelString = shortcut().toString( QKeySequence::NativeText ); - if ( !accelString.isEmpty() ) - setToolTip( QStringLiteral("%1 [%2]").arg( item.text, accelString ) ); - else - setToolTip( item.text ); -} - -void ToolBarButton::mouseDoubleClickEvent( QMouseEvent * /*event*/ ) -{ - emit buttonDoubleClicked( buttonID() ); -} - -/* PageViewToolBar */ - -static const int toolBarGridSize = 40; - -class ToolBarPrivate -{ -public: - ToolBarPrivate( PageViewToolBar * qq ) - : q( qq ) - { - } - - // rebuild contents and reposition then widget - void buildToolBar(); - void reposition(); - // compute the visible and hidden positions along current side - QPoint getInnerPoint() const; - QPoint getOuterPoint() const; - void selectButton( ToolBarButton * button ); - - PageViewToolBar * q; - - // anchored widget and side - QWidget * anchorWidget; - PageViewToolBar::Side anchorSide; - - // slide in/out stuff - QTimer * animTimer; - QPoint currentPosition; - QPoint endPosition; - bool hiding; - bool visible; - - // background pixmap and buttons - QPixmap backgroundPixmap; - QLinkedList< ToolBarButton * > buttons; -}; - -PageViewToolBar::PageViewToolBar( PageView * parent, QWidget * anchorWidget ) - : QWidget( parent ), d( new ToolBarPrivate( this ) ) -{ - // initialize values of the private data storage structure - d->anchorWidget = anchorWidget; - d->anchorSide = Left; - d->hiding = false; - d->visible = false; - - // create the animation timer - d->animTimer = new QTimer( this ); - connect( d->animTimer, &QTimer::timeout, this, &PageViewToolBar::slotAnimate ); - - // apply a filter to get notified when anchor changes geometry - d->anchorWidget->installEventFilter( this ); - - setContextMenuPolicy( Qt::ActionsContextMenu ); - addAction( parent->actionCollection()->action( QStringLiteral("options_configure_annotations") ) ); -} - -PageViewToolBar::~PageViewToolBar() -{ - // delete the private data storage structure - delete d; -} - -void PageViewToolBar::setItems( const QLinkedList &items ) -{ - // delete buttons if already present - if ( !d->buttons.isEmpty() ) - { - QLinkedList< ToolBarButton * >::iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; it != end; ++it ) - delete *it; - d->buttons.clear(); - } - - // create new buttons for given items - QLinkedList::const_iterator it = items.begin(), end = items.end(); - for ( ; it != end; ++it ) - { - ToolBarButton * button = new ToolBarButton( this, *it ); - connect(button, &ToolBarButton::clicked, this, &PageViewToolBar::slotButtonClicked); - connect(button, &ToolBarButton::buttonDoubleClicked, this, &PageViewToolBar::buttonDoubleClicked); - d->buttons.append( button ); - } - - // rebuild toolbar shape and contents - d->reposition(); -} - -void PageViewToolBar::setSide( Side side ) -{ - d->anchorSide = side; - - d->reposition(); -} - -void PageViewToolBar::showAndAnimate() -{ - // set parameters for sliding in - d->hiding = false; - - show(); - -#ifdef OKULAR_ANIMATE_REVIEW_TOOBAR - // start scrolling in - d->animTimer->start( 20 ); -#else - d->currentPosition = d->endPosition; - - move( d->currentPosition ); - - d->visible = true; -#endif -} - -void PageViewToolBar::hideAndDestroy() -{ - // set parameters for sliding out - d->hiding = true; - d->endPosition = d->getOuterPoint(); - -#ifdef OKULAR_ANIMATE_REVIEW_TOOBAR - // start scrolling out - d->animTimer->start( 20 ); -#else - d->currentPosition = d->endPosition; - - move( d->currentPosition ); - - d->visible = false; - deleteLater(); -#endif -} - -void PageViewToolBar::selectButton( int id ) -{ - ToolBarButton * button = nullptr; - if ( id >= 0 && id < d->buttons.count() ) - button = *(d->buttons.begin() + id); - else - { - QLinkedList< ToolBarButton * >::const_iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; !button && it != end; ++it ) - if ( (*it)->isChecked() ) - button = *it; - if ( button ) - button->setChecked( false ); - } - d->selectButton( button ); -} - -bool PageViewToolBar::eventFilter( QObject * obj, QEvent * e ) -{ - // if anchorWidget changed geometry reposition toolbar - if ( obj == d->anchorWidget && e->type() == QEvent::Resize ) - { - d->animTimer->stop(); - if ( d->hiding ) - deleteLater(); - else - d->reposition(); - } - - // don't block event - return false; -} - -void PageViewToolBar::paintEvent( QPaintEvent * e ) -{ - // paint the internal pixmap over the widget - QPainter p( this ); - p.drawImage( e->rect().topLeft(), d->backgroundPixmap.toImage(), e->rect() ); -} - -void PageViewToolBar::mousePressEvent( QMouseEvent * e ) -{ - // set 'dragging' cursor - if ( e->button() == Qt::LeftButton ) - setCursor( Qt::SizeAllCursor ); -} - -void PageViewToolBar::mouseMoveEvent( QMouseEvent * e ) -{ - if ( ( QApplication::mouseButtons() & Qt::LeftButton ) != Qt::LeftButton ) - return; - - // compute the nearest side to attach the widget to - QPoint parentPos = mapToParent( e->pos() ); - float nX = (float)parentPos.x() / (float)d->anchorWidget->width(), - nY = (float)parentPos.y() / (float)d->anchorWidget->height(); - if ( nX > 0.3 && nX < 0.7 && nY > 0.3 && nY < 0.7 ) - return; - bool LT = nX < (1.0 - nY); - bool LB = nX < (nY); - Side side = LT ? ( LB ? Left : Top ) : ( LB ? Bottom : Right ); - - // check if side changed - if ( side == d->anchorSide ) - return; - - d->anchorSide = side; - d->reposition(); - emit orientationChanged( (int)side ); -} - -void PageViewToolBar::mouseReleaseEvent( QMouseEvent * e ) -{ - // set normal cursor - if ( e->button() == Qt::LeftButton ) - setCursor( Qt::ArrowCursor ); -} - -void ToolBarPrivate::buildToolBar() -{ - int buttonsNumber = buttons.count(), - parentWidth = anchorWidget->width(), - parentHeight = anchorWidget->height(), - myCols = 1, - myRows = 1; - - // 1. find out columns and rows we're going to use - bool topLeft = anchorSide == PageViewToolBar::Left || anchorSide == PageViewToolBar::Top; - bool vertical = anchorSide == PageViewToolBar::Left || anchorSide == PageViewToolBar::Right; - if ( vertical ) - { - myCols = 1 + (buttonsNumber * toolBarGridSize) / - (parentHeight - toolBarGridSize); - myRows = (int)ceil( (float)buttonsNumber / (float)myCols ); - } - else - { - myRows = 1 + (buttonsNumber * toolBarGridSize) / - (parentWidth - toolBarGridSize); - myCols = (int)ceil( (float)buttonsNumber / (float)myRows ); - } - - // 2. compute widget size (from rows/cols) - int myWidth = myCols * toolBarGridSize, - myHeight = myRows * toolBarGridSize, - xOffset = (toolBarGridSize - ToolBarButton::buttonSize) / 2, - yOffset = (toolBarGridSize - ToolBarButton::buttonSize) / 2; - - if ( vertical ) - { - myHeight += 16; - myWidth += 4; - yOffset += 12; - if ( anchorSide == PageViewToolBar::Right ) - xOffset += 4; - } - else - { - myWidth += 16; - myHeight += 4; - xOffset += 12; - if ( anchorSide == PageViewToolBar::Bottom ) - yOffset += 4; - } - - bool prevUpdates = q->updatesEnabled(); - q->setUpdatesEnabled( false ); - - // 3. resize pixmap, mask and widget - QBitmap mask( myWidth + 1, myHeight + 1 ); - backgroundPixmap = QPixmap( myWidth + 1, myHeight + 1 ); - backgroundPixmap.fill(Qt::transparent); - q->resize( myWidth + 1, myHeight + 1 ); - - // 4. create and set transparency mask // 4. draw background - QPainter maskPainter( &mask); - mask.fill( Qt::white ); - maskPainter.setBrush( Qt::black ); - if ( vertical ) - maskPainter.drawRoundRect( topLeft ? -10 : 0, 0, myWidth + 11, myHeight, 2000 / (myWidth + 10), 2000 / myHeight ); - else - maskPainter.drawRoundRect( 0, topLeft ? -10 : 0, myWidth, myHeight + 11, 2000 / myWidth, 2000 / (myHeight + 10) ); - maskPainter.end(); - q->setMask( mask ); - - // 5. draw background - QPainter bufferPainter( &backgroundPixmap ); - bufferPainter.translate( 0.5, 0.5 ); - QPalette pal = q->palette(); - // 5.1. draw horizontal/vertical gradient - QLinearGradient grad; - switch ( anchorSide ) - { - case PageViewToolBar::Left: - grad = QLinearGradient( 0, 1, myWidth + 1, 1 ); - break; - case PageViewToolBar::Right: - grad = QLinearGradient( myWidth + 1, 1, 0, 1 ); - break; - case PageViewToolBar::Top: - grad = QLinearGradient( 1, 0, 1, myHeight + 1 ); - break; - case PageViewToolBar::Bottom: - grad = QLinearGradient( 1, myHeight + 1, 0, 1 ); - break; - } - grad.setColorAt( 0, pal.color( QPalette::Active, QPalette::Button ) ); - grad.setColorAt( 1, pal.color( QPalette::Active, QPalette::Light ) ); - bufferPainter.setBrush( QBrush( grad ) ); - // 5.2. draw rounded border - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Dark ).lighter( 140 ) ); - bufferPainter.setRenderHints( QPainter::Antialiasing ); - if ( vertical ) - bufferPainter.drawRoundRect( topLeft ? -10 : 0, 0, myWidth + 10, myHeight, 2000 / (myWidth + 10), 2000 / myHeight ); - else - bufferPainter.drawRoundRect( 0, topLeft ? -10 : 0, myWidth, myHeight + 10, 2000 / myWidth, 2000 / (myHeight + 10) ); - // 5.3. draw handle - bufferPainter.translate( -0.5, -0.5 ); - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Mid ) ); - if ( vertical ) - { - int dx = anchorSide == PageViewToolBar::Left ? 2 : 4; - bufferPainter.drawLine( dx, 6, dx + myWidth - 8, 6 ); - bufferPainter.drawLine( dx, 9, dx + myWidth - 8, 9 ); - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Light ) ); - bufferPainter.drawLine( dx + 1, 7, dx + myWidth - 7, 7 ); - bufferPainter.drawLine( dx + 1, 10, dx + myWidth - 7, 10 ); - } - else - { - int dy = anchorSide == PageViewToolBar::Top ? 2 : 4; - bufferPainter.drawLine( 6, dy, 6, dy + myHeight - 8 ); - bufferPainter.drawLine( 9, dy, 9, dy + myHeight - 8 ); - bufferPainter.setPen( pal.color( QPalette::Active, QPalette::Light ) ); - bufferPainter.drawLine( 7, dy + 1, 7, dy + myHeight - 7 ); - bufferPainter.drawLine( 10, dy + 1, 10, dy + myHeight - 7 ); - } - bufferPainter.end(); - - // 6. reposition buttons (in rows/col grid) - int gridX = 0, - gridY = 0; - QLinkedList< ToolBarButton * >::const_iterator it = buttons.begin(), end = buttons.end(); - for ( ; it != end; ++it ) - { - ToolBarButton * button = *it; - button->move( gridX * toolBarGridSize + xOffset, - gridY * toolBarGridSize + yOffset ); - button->show(); - if ( ++gridX == myCols ) - { - gridX = 0; - gridY++; - } - } - - q->setUpdatesEnabled( prevUpdates ); -} - -void ToolBarPrivate::reposition() -{ - // note: hiding widget here will gives better gfx, but ends drag operation - // rebuild widget and move it to its final place - buildToolBar(); - if ( !visible ) - { - currentPosition = getOuterPoint(); - endPosition = getInnerPoint(); - } - else - { - currentPosition = getInnerPoint(); - endPosition = getOuterPoint(); - } - q->move( currentPosition ); - - // repaint all buttons (to update background) - QLinkedList< ToolBarButton * >::const_iterator it = buttons.begin(), end = buttons.end(); - for ( ; it != end; ++it ) - (*it)->update(); -} - -QPoint ToolBarPrivate::getInnerPoint() const -{ - // returns the final position of the widget - QPoint newPos; - switch ( anchorSide ) - { - case PageViewToolBar::Left: - newPos = QPoint( 0, ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Top: - newPos = QPoint( ( anchorWidget->width() - q->width() ) / 2, 0 ); - break; - case PageViewToolBar::Right: - newPos = QPoint( anchorWidget->width() - q->width(), ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Bottom: - newPos = QPoint( ( anchorWidget->width() - q->width()) / 2, anchorWidget->height() - q->height() ); - break; - } - return newPos + anchorWidget->pos(); -} - -QPoint ToolBarPrivate::getOuterPoint() const -{ - // returns the point from which the transition starts - QPoint newPos; - switch ( anchorSide ) - { - case PageViewToolBar::Left: - newPos = QPoint( -q->width(), ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Top: - newPos = QPoint( ( anchorWidget->width() - q->width() ) / 2, -q->height() ); - break; - case PageViewToolBar::Right: - newPos = QPoint( anchorWidget->width(), ( anchorWidget->height() - q->height() ) / 2 ); - break; - case PageViewToolBar::Bottom: - newPos = QPoint( ( anchorWidget->width() - q->width() ) / 2, anchorWidget->height() ); - break; - } - return newPos + anchorWidget->pos(); -} - -void PageViewToolBar::slotAnimate() -{ - // move currentPosition towards endPosition - int dX = d->endPosition.x() - d->currentPosition.x(), - dY = d->endPosition.y() - d->currentPosition.y(); - dX = dX / 6 + qMax( -1, qMin( 1, dX) ); - dY = dY / 6 + qMax( -1, qMin( 1, dY) ); - d->currentPosition.setX( d->currentPosition.x() + dX ); - d->currentPosition.setY( d->currentPosition.y() + dY ); - - // move the widget - move( d->currentPosition ); - - // handle arrival to the end - if ( d->currentPosition == d->endPosition ) - { - d->animTimer->stop(); - if ( d->hiding ) - { - d->visible = false; - deleteLater(); - } - else - { - d->visible = true; - } - } -} - -void PageViewToolBar::slotButtonClicked() -{ - ToolBarButton * button = qobject_cast( sender() ); - d->selectButton( button ); -} - -void ToolBarPrivate::selectButton( ToolBarButton * button ) -{ - if ( button ) - { - // deselect other buttons - QLinkedList< ToolBarButton * >::const_iterator it = buttons.begin(), end = buttons.end(); - for ( ; it != end; ++it ) - if ( *it != button ) - (*it)->setChecked( false ); - // emit signal (-1 if button has been unselected) - emit q->toolSelected( button->isChecked() ? button->buttonID() : -1 ); - } -} - -void PageViewToolBar::setToolsEnabled( bool on ) -{ - QLinkedList< ToolBarButton * >::const_iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; it != end; ++it ) - (*it)->setEnabled( on ); -} - -void PageViewToolBar::setTextToolsEnabled( bool on ) -{ - QLinkedList< ToolBarButton * >::const_iterator it = d->buttons.begin(), end = d->buttons.end(); - for ( ; it != end; ++it ) - if ( (*it)->isText() ) - (*it)->setEnabled( on ); -} - #include "moc_pageviewutils.cpp" diff --git a/ui/side_reviews.cpp b/ui/side_reviews.cpp --- a/ui/side_reviews.cpp +++ b/ui/side_reviews.cpp @@ -61,7 +61,7 @@ QTextDocument document; document.setHtml( i18n( "

No annotations

" - "To create new annotations press F6 or select Tools -> Review" + "To create new annotations press F6 or select Tools -> Annotations" " from the menu.
" ) ); document.setTextWidth( width() - 50 ); @@ -141,7 +141,7 @@ // - add separator toolBar->addSeparator(); // - add Current Page Only button - QAction * curPageOnlyAction = toolBar->addAction( QIcon::fromTheme( QStringLiteral("arrow-down") ), i18n( "Show reviews for current page only" ) ); + QAction * curPageOnlyAction = toolBar->addAction( QIcon::fromTheme( QStringLiteral("arrow-down") ), i18n( "Show annotations for current page only" ) ); curPageOnlyAction->setCheckable( true ); connect(curPageOnlyAction, &QAction::toggled, this, &Reviews::slotCurrentPageOnly); curPageOnlyAction->setChecked( Okular::Settings::currentPageOnly() ); diff --git a/ui/toggleactionmenu.h b/ui/toggleactionmenu.h new file mode 100644 --- /dev/null +++ b/ui/toggleactionmenu.h @@ -0,0 +1,135 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hurka * + * * + * Inspired by and replacing toolaction.h by: * + * Copyright (C) 2004-2006 by Albert Astals Cid * + * * + * 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 TOGGLEACTIONMENU_H +#define TOGGLEACTIONMENU_H + +#include +#include +#include + +/** + * @brief A KActionMenu, with allows to set the default action of its toolbar buttons. + * + * Usually, a KActionMenu creates toolbar buttons which reflect its own action properties + * (icon, text, tooltip, checked state,...), as it is a QAction itself. + * ToggleActionMenu will use its own action properties only when plugged as submenu in another menu. + * But the default action of the toolbar buttons can easily be changed with the slot setDefaultAction(). + * + * Naming: The user can *Toggle* the checked state of an *Action* by directly clicking the toolbar button, + * but can also open a *Menu*. + * + * @par Intention + * Setting the default action of the toolbar button can be useful for: + * * Providing the most propably needed entry of a menu directly on the menu button. + * * Showing the last used menu entry on the menu button, including its checked state. + * The advantage is that the user often does not need to open the menu, + * and that the toolbar button shows additional information + * like checked state or the user's last selection. + * + * This shall replace the former ToolAction in Okular, + * while beeing flexible enough for other (planned) action menus. + */ +class ToggleActionMenu : public KActionMenu +{ + Q_OBJECT + +public: + /** + * Defines how the menu behaves. + */ + enum MenuLogic { + DefaultLogic = 0x0, + /** + * Automatically makes the triggered action the default action, even if in a submenu. + * When a toolbar button is constructed, + * the default action is set to the default action set with setDefaultAction() before, + * otherwise to the first checked action in the menu, + * otherwise to the action suggested with suggestDefaultAction(). + */ + ImplicitDefaultAction = 0x1 + }; + + explicit ToggleActionMenu( QObject *parent ); + ToggleActionMenu( const QString &text, QObject * parent ); + /** + * These are the usual constructors for KActionMenu. + * + * @note + * @p text and @p icon are used only if this menu is a submenu in another menu. + * To set the appearance of the toolbar buttons, use setDefaultAction. + */ + ToggleActionMenu( const QIcon &icon, + const QString &text, + QObject *parent, + QToolButton::ToolButtonPopupMode popupMode = QToolButton::MenuButtonPopup, + MenuLogic logic = DefaultLogic + ); + + QWidget *createWidget( QWidget *parent ) override; + + /** + * Returns the current default action of the toolbar buttons. + * + * In ImplicitDefaultAction mode, + * when the default action was not yet set with setDefaultAction(), + * it will determine it from the first checked action in the menu, + * otherwise from the action set with suggestDefaultAction(). + */ + QAction *defaultAction(); + + /** + * Suggests a default action to be used as fallback. + * + * It will be used if the default action is not determined another way. + */ + void suggestDefaultAction( QAction * action ); + +public slots: + /** + * Sets the default action of the toolbar buttons. + * + * This action will be triggered by clicking directly on the toolbar buttons. + * It will also set the text, icon, checked state, etc. of the toolbar buttons. + * + * @note + * The default action will not set the enabled state or popup mode of the menu buttons. + * These properties are still set by the corresponding properties of this ToggleActionMenu. + * + * @note + * The action will not be added to the menu, + * it usually makes sense to addAction() it before to setDefaultAction() it. + */ + void setDefaultAction( QAction *action ); + +private: + QAction *m_defaultAction; + QAction *m_suggestedDefaultAction; + QList< QPointer< QToolButton > > m_buttons; + MenuLogic m_menuLogic; + + /** + * Returns the first checked action in menu(), + * or nullptr if no action is checked. + */ + QAction *checkedAction() const; + +private slots: + /** + * Updates the toolbar buttons, using both the default action and properties of this menu itself. + * + * This ensures that the toolbar buttons reflect e. g. a disabled state of this menu. + */ + void updateButtons(); +}; + +#endif // TOGGLEACTIONMENU_H diff --git a/ui/toggleactionmenu.cpp b/ui/toggleactionmenu.cpp new file mode 100644 --- /dev/null +++ b/ui/toggleactionmenu.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2019 by David Hurka * + * * + * Inspired by and replacing toolaction.h by: * + * Copyright (C) 2004-2006 by Albert Astals Cid * + * * + * 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 "toggleactionmenu.h" + +#include +#include + +ToggleActionMenu::ToggleActionMenu( QObject * parent ) + : ToggleActionMenu( QIcon(), QString(), parent ) +{ +} + +ToggleActionMenu::ToggleActionMenu( const QString &text, QObject * parent ) + : ToggleActionMenu( QIcon(), text, parent ) +{ +} + +ToggleActionMenu::ToggleActionMenu( const QIcon &icon, + const QString &text, + QObject * parent, + QToolButton::ToolButtonPopupMode popupMode, + MenuLogic logic ) + : KActionMenu( icon, text, parent ), + m_defaultAction( nullptr ), + m_suggestedDefaultAction( nullptr ), + m_menuLogic( logic ) +{ + connect( this, &QAction::changed, this, &ToggleActionMenu::updateButtons ); + + if ( popupMode == QToolButton::DelayedPopup ) + { + setDelayed( true ); + } + else if ( popupMode == QToolButton::InstantPopup ) + { + setDelayed( false ); + setStickyMenu( true ); + } + else + { + setDelayed( false ); + setStickyMenu( false ); + } + + if ( logic & ImplicitDefaultAction ) + { + connect( menu(), &QMenu::triggered, this, &ToggleActionMenu::setDefaultAction ); + } +} + +QWidget * ToggleActionMenu::createWidget( QWidget * parent ) +{ + QToolButton * button = qobject_cast< QToolButton * >( KActionMenu::createWidget( parent ) ); + if ( !button ) { + // This function is used to add a button into the toolbar. + // KActionMenu will plug itself as QToolButton. + // So, if no QToolButton was returned, this was not called the intended way. + return button; + } + + // Remove this menu action from the button, + // so it doesn't compose a menu of this menu action and its own menu. + button->removeAction( this ); + // The button has lost the menu now, let it use the correct menu. + button->setMenu( menu() ); + + m_buttons.append( QPointer< QToolButton >( button ) ); + + // Apply other properties to the button. + updateButtons(); + + return button; +} + +void ToggleActionMenu::setDefaultAction( QAction *action ) +{ + m_defaultAction = action; + updateButtons(); +} + +void ToggleActionMenu::suggestDefaultAction( QAction *action ) +{ + m_suggestedDefaultAction = action; +} + +QAction * ToggleActionMenu::checkedAction() const +{ + for ( QAction * a : menu()->actions() ) + { + if ( a->isChecked() ) + { + return a; + } + } + return nullptr; +} + +void ToggleActionMenu::updateButtons() +{ + for ( QPointer< QToolButton > button : qAsConst( m_buttons ) ) + { + if ( button ) + { + button->setDefaultAction( defaultAction() ); + + // Override some properties of the default action, + // where the property of this menu makes more sense. + button->setEnabled( isEnabled() ); + + if (delayed()) + { + button->setPopupMode(QToolButton::DelayedPopup); + } + else if (stickyMenu()) + { + button->setPopupMode(QToolButton::InstantPopup); + } + else + { + button->setPopupMode(QToolButton::MenuButtonPopup); + } + } + } +} + +QAction * ToggleActionMenu::defaultAction() +{ + if ( ( m_menuLogic & ImplicitDefaultAction ) && !m_defaultAction ) + { + m_defaultAction = checkedAction(); + } + if ( !m_defaultAction ) + { + m_defaultAction = m_suggestedDefaultAction; + } + return m_defaultAction; +} diff --git a/ui/toolaction.h b/ui/toolaction.h deleted file mode 100644 --- a/ui/toolaction.h +++ /dev/null @@ -1,41 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2004-2006 by Albert Astals Cid * - * * - * 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 TOOLACTION_H -#define TOOLACTION_H - -#include -#include - -#include - -class QToolButton; - -class ToolAction : public KSelectAction -{ - Q_OBJECT - - public: - explicit ToolAction( QObject *parent = nullptr ); - virtual ~ToolAction(); - - void addAction( QAction *action ); - - protected: - QWidget* createWidget( QWidget *parent ) override; - - private Q_SLOTS: - void slotNewDefaultAction( QAction *action ); - - private: - QList< QPointer< QToolButton > > m_buttons; - QList< QAction * > m_actions; -}; - -#endif diff --git a/ui/toolaction.cpp b/ui/toolaction.cpp deleted file mode 100644 --- a/ui/toolaction.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2004-2006 by Albert Astals Cid * - * * - * 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 "toolaction.h" - -#include -#include -#include - -#include - -ToolAction::ToolAction( QObject *parent ) - : KSelectAction( parent ) -{ - setText( i18n( "Selection Tools" ) ); -} - -ToolAction::~ToolAction() -{ -} - -void ToolAction::addAction( QAction *action ) -{ - bool setDefault = !m_buttons.isEmpty() ? m_buttons.first()->menu()->actions().isEmpty() : false; - foreach ( QToolButton *button, m_buttons ) - if ( button ) - { - button->menu()->addAction( action ); - if ( setDefault ) - button->setDefaultAction( action ); - } - m_actions.append( action ); -} - -QWidget* ToolAction::createWidget( QWidget *parent ) -{ - QToolBar *toolBar = qobject_cast< QToolBar * >( parent ); - if ( !toolBar ) - return nullptr; - - QToolButton *button = new QToolButton( toolBar ); - button->setAutoRaise( true ); - button->setFocusPolicy( Qt::NoFocus ); - button->setIconSize( toolBar->iconSize() ); - button->setToolButtonStyle( toolBar->toolButtonStyle() ); - button->setPopupMode( QToolButton::MenuButtonPopup ); - button->setMenu( new QMenu( button ) ); - button->setCheckable( true ); - connect(toolBar, &QToolBar::iconSizeChanged, button, &QToolButton::setIconSize); - connect(toolBar, &QToolBar::toolButtonStyleChanged, button, &QToolButton::setToolButtonStyle); - connect(button, &QToolButton::triggered, toolBar, &QToolBar::actionTriggered); - connect( button->menu(), &QMenu::triggered, this, &ToolAction::slotNewDefaultAction ); - - m_buttons.append( button ); - - if ( !m_actions.isEmpty() ) - { - button->setDefaultAction( m_actions.first() ); - foreach ( QAction *action, m_actions ) - { - button->menu()->addAction( action ); - if ( action->isChecked() ) - button->setDefaultAction( action ); - } - button->setToolTip( i18n("Click to use the current selection tool\nClick on the arrow to choose another selection tool") ); - } - - return button; -} - -void ToolAction::slotNewDefaultAction( QAction *action ) -{ - foreach ( QToolButton *button, m_buttons ) - if ( button ) - { - button->setDefaultAction( action ); - button->setToolTip( i18n("Click to use the current selection tool\nClick on the arrow to choose another selection tool") ); - } -} - -#include "moc_toolaction.cpp"